Union-Find Set 并查集 详解 [基本模板]
来源:互联网 发布:淘宝护肤品店铺标志 编辑:程序博客网 时间:2024/05/22 18:32
What to solve? Which regard is it? Where to optimizate? Meanwhile, it is worth nothing that we are careful.
/* name: Union-Find Set by Tangent Chang in 107/3/20 in the Republic of China*/
并查集,是一种树型的数据结构,用于处理一些不相交的合并问题。
1.合并两个不相交集合;
2.判断两个元素是否属于同一个集合;
3.路径压缩,优化时间。
1.采用路径压缩;
2.采用启发式合并:让深度较小的树成为深度较大的树的子树。
·非递归写法有时比递归写法要慢,所以两种写法都得会。
·连接、找父节点都要注意节点的移动和权值的变换,这个根据题目要求来编写。
/* name: Union-Find Set by Tangent Chang in 107.3.20 in the Republic of China*/int father[maxn];int n;// 有人喜欢把father[]换成fat[],pa[]或pre[]。void Init() { for (int i = 0; i < n; ++i) { father[i] = i; }}// 有人喜欢把初始化函数写成makeSet(),表示标记设置之意。/*// 递归(有速度加持)。int getFather(const int &v) { if (father[v] == v) return v; else return getFather(father[v]);}// 递归另一种写法。int getFather(const int &v) { if (father[v] != v) { int root = getFather(father[v]); return father[v] = root; } else return v;}// 非递归,且不带路径压缩。int getFather(const int &v) { int r = v; while(r != father[r]) r = father[r]; return r;}*/// 非递归,路径压缩int getFather(const int &v) { int t1 = v, t2; while (v != father[v]) v = father[v]; while (t1 != father[t1]) { // 沿途上所有结点的父亲改成根。这一步是顺便的,不增加时间复杂度,却使得今后的操作比较快。这个优化称为路径压缩。 t2 = father[t1]; father[t1] = v; t1 = t2; } return v;}// 有人喜欢把getFather()换成Find(),findRoot()或findSet()。// 归并:把节点i、节点j放到同一个根底下。void merge(const int &i, const int &j) { int x = getFather(i); int y = getFather(j); if (x != y) // 可选,主要是为了防止getFather()路径压缩的时候出现死循环。 father[x] = y; // 有向图注意顺序,该行代码含义:a->b。}// 查询:查询节点i跟节点j是否在同一根下。bool judge(const int &i, const int &j) { if (getFather(i) == getFather(j)) return true; else return false;}// 有人喜欢把merge()换成Union(),union_nodes()或connect(),还有一些喜欢把judge()的内容放到union()里写。
优化思路:
merge函数可以采用启发式合并,思路就是把深度较小的那颗子树并到深度较大的那颗子树上。int father[maxn];int n;void Init() { for (int i = 0; i <= n; ++i) { rank[i] = 0; father[i] = i; }}int getFather(const int &x) { int px = x , i ; while ( px != father[px]) // find root px = father[px]; while ( x != px ) { // path compression i = father [ x ]; father [ x ] = px ; x = i; } return px ;}void merge(const int &x, const int &y) { // 下面还有一种写法 x = getFather(x); y = getFather(y); if (rank[x] > rank[y]) father[y] = x; else { father[x] = y; if (rank[x] == rank[y]) rank[y]++; } }bool judge(const int &i, const int &j) { if (getFather(i) == getFather(j)) return true; else return false;}
并查集可以离散化。
–有兴趣的coder可以自行百度。
并查集可以用类的思想去想,并添加一个深度数组rank[]。
就是启发式合并中用rank[i]表示i的秩,用秩来代替深度。
下面代码没写构造函数,有兴趣的coder自行百度。
int father[maxn], rank[maxn];void Init(const int &v) { father[v] = -1; rank[v] = 0;}int getFather(const int &v) { int t1 = v; while (father[t1] != -1) t1 = father[t1]; while (v!=t1) { int t2 = father[v]; father[v] = t1; v = t2; } return t1; } void merge(const int &a, const int &b) { int t1 = getFathet1(a); int t2 = getFathet1(b); if(rank[t1] > rank[t2]) father[t2] = t1; else father[t1] = t2; if(rank[t1] == rank[t2]) ++rank[t2]; }bool judge(const int &i, const int &j) { if (getFather(i) == getFather(j)) return true; else return false;}
/* 另一种写法:*/int f[maxn], rank[maxn], num[maxn];void Init() { for (int i = 0; i <= n; ++i) { rank[i] = 1; num[i] = 1; father[i] = i; }}// f[]数组存放根节点,rank[]数组来存放根节点的深度,num[]数组来存放节点个数,rank[]数组和num[]数组的初始化都应为1// 启发式合并:void merge(int x, int y){ fx = getFather(x); fy = getFather(y); if (fx == fy) return; if (rank[fx] > rank[fy]) { father[fy] = fx; num[fx] += num[fy]; } else { father[fx] = fy; num[fy] += num[fx]; if (rank[fx] == rank[fy]) { ++rank[fy]; } }}// 路径压缩:int getFather(int x) { if(father[x] == x) return x; else return father[x] = getFather(father[x]);}
// 仍有一种写法:int father[maxn];void Init() { for(int i = 0; i < n; ++i) father[i] = -1;}int getFather(int x) { if (father[x] < 0) return x; father[x] = getFather(father[x]); return father[x];}int getFather(int x) { int p = x, t; while (father[p] >= 0) p = father[p]; while (x != p) { t = father[x]; father[x] = p; x = t; } return x;}void merge(int x, int y) { x = getFather(x); y = getFather(y) if (x == y) return; if (father[x] < father[y]) { father[x] += father[y]; father[y] = x; } else { father[y] += father[x]; father[x] = y; }}bool judge(const int &i, const int &j) { if (getFather(i) == getFather(j)) return true; else return false;}
当考察并查集某个节点的移动次数或者某点权值的时候,需要注意一下细节:
1.初始化
2.连接、找父节点都要注意节点的移动和权值的变换
int father[maxn]; // 存放每个节点的根节点int num[maxn]; // 记录节点的转移次数int cnt[maxn]; // 某点的权值void Init() { for (int i = 1; i <= n; ++i) { father[i] = i; } memset(num, 0, sizeof(num)); memset(cnt, 0, sizeof(cnt));}int getFather(const int &x) { if (father[x] != x){ int tmp = father[x]; father[x] = getFather(father[x]); num[x] += num[tmp]; // x点转移的次数是他的转移次数加上父节点的转移次数。 } return father[x];}void merge(const int &x, const int &y) { int xx = getFather(x), yy = getFather(y); if (xx != yy){ father[xx] = yy; ++num[xx]; // 开始转移了一次! cnt[yy] += cnt[xx]; // 权值转移了。 }}bool judge(const int &i, const int &j) { if (getFather(i) == getFather(j)) return true; else return false;}
并查集简介
并查集,就是Union-Find Set,也称不相交集合 (Disjoint Set)。她是一种树型的数据结构,用于处理一些不相交的合并问题。
总结
一般问题都是围绕着并查集的两个主要操作,合并和查找做文章,根据具体应用,增加一些信息,增加一些逻辑,例如转移次数,或者是根据问题特征选择使用合适的优化策略,按秩合并(启发式策略)以及路径压缩。
给我点个顶吧!d=====( ̄▽ ̄*)b
3 0
- Union-Find Set 并查集 详解 [基本模板]
- 并查集(Union-Find Set)模板
- 并查集(Union-Find Set)
- 并查集 Union-Find-Set
- 并查集(Union-Find Set)小结
- 并查集(union-find)模板
- disjoint set (union-find set) (并查集)
- 并查集(Union-Find)算法详解
- 并查集(union-find set or DisjointSets)
- 一、并查集 (Union-Find Set)
- 并查集(union-find set)与Kruskal算法
- 并查集(Union-Find)
- Union Find 并查集
- 并查集Union--Find
- 并查集Union-Find
- union-find(并查集)
- 并查集- Union-Find
- union find(并查集)
- 05数组相关
- 【学习笔记】http协议
- datatables添加checkbox复选框
- 设计模式之基础-UML
- .NET 关于从客户端(...)中检测到有潜在危险的Request.Form 值的处理办法 找不到方法:System.Web.UnvalidatedRequestValues报错的处理方案(待完善)
- Union-Find Set 并查集 详解 [基本模板]
- 从零开始学SpringBoot(2)全局异常捕捉
- 生成最少树
- 10059---java反射实现bean的copy
- 【0321】供应链、产品、获客
- Caffe学习日记9
- 【LeetCode】529. Minesweeper
- React学习笔记(二)
- 442. Find All Duplicates in an Array