并查集及其优化

Posted xylee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集及其优化相关的知识,希望对你有一定的参考价值。

并查集

概述

性质

  • 一种树形结构
  • 并查集算法不支持分割一个集合

元素

  • 代表元
    • 集合中的元素,用来代表这个集合
    • 一个集合内的所有元素组织成以代表元为根的树形结构
  • parent[x]
    • 对于每一个元素,parent[x]指向x在树形结构上的父亲节点。如果x是根节点,则令parent[x] = x

操作

  • MakeSet
    • 初始化并查集
    • 设置一个代表
    •   function MakeSet(x) // 参数 => 选定的代表元
        x.parent := x
      
  • Find
    • 确定元素属于哪一个子集,返回元素所属集合的代表元
    • 方法 => 可以沿着parent[x]不断在树形结构中向上移动,直到到达根节点
    • 判断两个元素是否属于同一集合,只要看他们的代表元是否相同即可
    •   function Find(x) // 参数 => 带查找的元素
        	if x.parent == x // 到达根节点
        		return x
        	else
        		return Find(x.parent)
      
  • Union
    • 将两个子集合并成同一个集合
    •   function Union(x, y)
        	xRoot := Find(x)
        	yRoot := Find(y)
        	xRoot.parent := yRoot
      

并查集Uuion优化

  • 并查集最基础的表示方法为数组表示
  • 性能劣于链表法,因为创建的树可能会严重不平衡,某些树枝深度太高
    • 平衡优化
      • 按秩合并
        • 总是将更小的树连接至更大的树上

        • 秩 : 深度

        • 影响运行时间的是树深度,更小的树添加到更深的树的根上不会增加秩除非他们同秩

        • 单个元素秩定义为0,当两棵秩同为r的树联合时,他们的秩为r+1

        • 最坏时间复杂度O(logN)

        •   function MakeSet(x) // 初始化
                x.parent := x
                x.rank := 0 // 并查集树结构每个节点包含秩信息
          
        •   function Union(x, y) // 合并
          	  xRoot := Find(x)
          	  yRoot := Find(y)
          	  if xRoot == yRoot
                return
          	  // x和y不在同一个集合,合并他们。
          	  if xRoot.rank < yRoot.rank
                xRoot.parent := yRoot
          	  else if xRoot.rank > yRoot.rank
          	      yRoot.parent := xRoot
          	  else
          	      yRoot.parent := xRoot
          	      xRoot.rank := xRoot.rank + 1
          
      • 路径压缩
        • 执行"查找"时扁平化树结构的方法
        • 路径上每个节点都可以直接连接到根上
        • Find递归地经过树,改变每一个节点的引用到根节点,得到的树更加扁平,为以后直接或者间接引用节点的操作加速
        •   function Find(x)
            	if x.parent != x
            		x.parent := Find(x.parent) // 将每个节点引用到根节点
            	return x.parent
          
        • 渐进最优算法 : Fredman和Saks在1989年解释了O(N)的平均时间可以获得任何并查集

主要操作

  • 上面的操作都是假设元素分别独属于一个独立的集合里
  • 合并两个不相交集合
    • 设置一个parent数组,表示x的双亲编号
    • 合并两个不相交集合的办法就是找到其中一个集合的始祖,将另外一个集合的始祖的父亲指向它
    •   void Union(int x, int y) {
        	xRoot = Find(x); // 找到始祖
        	yRoot = Find(y); // 找到始祖
        	if (xRoot != yRoot)
        		parent[xRoot] = yRoot; // x -> y
        }
      
  • 判断两个元素是否属于同一个集合
    • 寻找始祖,比较始祖是否相同
    •   bool same(int x, int y) {
        	return Find(x) == Find(y); // 比较始祖是否相同
        }
      

并查集的优化

  • 路径压缩
    •   int getfather(int v) {
        	if (parent[v] == v) // 根节点
        		return v; // 返回始祖
        	else {
        		parent[v] = getfather(parent[v]); // 路径压缩【递归】
        		return parent[v]; // 返回并查集根节点
        	}
        }
      
  • Rank合并
    • 将元素所在深度最小的集合合并到元素所在深度最大的集合
    •   void judge(int x, int y) {
        	// 寻找始祖
        	xRoot = Find(x);
        	yRoot = Find(y);
        	if (rank[xRoot] > rank[yRoot])
        		parent[yRoot] = xRoot;
        	else {
        		parent[xRoot] = yRoot; // 以yRoot为始祖
        		if (rank[xRoot] == rank[yRoot])
        			++rank[yRoot]; // 重要的是始祖的rank,只修改始祖的,其他的不用管
        	}
        }
      
    • 初始化
      memset(rank, 0, sizeof(int)*rank_size);
      

时空复杂度

  • 时间复杂度
    • 同时使用路径压缩、按秩(rank)合并优化的程序每个操作的平均时间复杂度为O(N)
  • 空间复杂度
    • O(N) N为元素数量

以上是关于并查集及其优化的主要内容,如果未能解决你的问题,请参考以下文章

[每日算法] 并查集数据结构及其实例-- day15

[每日算法] 并查集数据结构及其实例-- day15

[每日算法] 并查集数据结构及其实例-- day15

树--12---并查集

力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集)

关于并查集的一切全在这里了