基础
What is a Union-Finding?
It is a data structure
A data structure
When?
解决图问题
use case:图是在动态变化
Why?
代码极其短
hit use case 时间复杂度
关注点
两个集合是不是联通
集合==》联通力量
性质:每一个集合都有一个代表
思考方式
集合代表着帮派
集合的代表元素代表帮派老大
API
UnionFind() constructor 构造函数O(n), n as number of node
union(int a, int b):把a加入b的集合,或者把b加入a的集合
int find(a):找到a所在集合的代表元素
时间复杂度
Quick Union -- union find
find: O(n)
union: O(n)
Quick Find -- union find
find: O(1)
union: O(n)
Weighted/Ranked/OtherRanked Union Find(Tree Array Based)
find: O(log n)
union O(log n)
Weight Union Find with Path Compression
find: O*(1)
union: O*(1)
where *: O(loglog(n)) > O(1)
How
Union - Find用数组实现的数据结构
假设长度是n -> int[] laoda
laoda[i] 代表i的老大是谁
count:一共有多少个帮派
int[] size -> size[i] 代表i所在的帮派有多少
How: Union Find底板(2min!!!)
class UnionFind{
int[] laoda; //有几个帮派的老大, 可能是需要laodaMap map<你的类型,帮派的老大>
int[] size; //几个帮派的大小
int count;
public UnionFind(int n){
this.laoda = new int[n];
this.size = new int[n];
this.count = n;
for (int i =; i< n; i++) {
laoda[i] = i;
size[i] = 1;
}
}
public int find(int a) {
}
public void union(int a, int b) {
}
}
UF时空复杂度
union,find 都是O*(1)
constructor: both O(n)
面试里注意点的和基本逻辑
对于find(a):找到的是真老大
union(a, b): a,b 要先检查需不需要union,如果老大都一样,就不需要用union了
第一种Union-Find: Quick Union
union(a, b)--> 把a加入b
example:
laoda: {0, 1, 2},
union(2, 1),
laoda = {0,1,1}
union(1, 0)
laoda: {0, 0, 1}
其实0,1,2,都在一个联通分量里面,但是观察老大是分级别的
谁是真老大?
真老大的特点laoda[zhenlaoda] = zhenlaoda
quick union:
union 一时爽,find的时候要溯源
public int find(int a) { //这个是用来找真正的老大,就是前面提到的溯源
while (a != laoda[a]) {
a = laoda[a];
}
return a;
}
public void union(int a, int b){//注意这里是把a加入b
int alaoda = find(a);
int blaoda = find(b);
if (alaoda == blaoda) return;
laoda[a] = b;
count--;
size[blaoda] += size[alaoda];
}
TC&SC:
find: O(n)
union: O(n)
第二种Union Find: Quick Find
Find O(1)
example:
laoda: {0, 1, 2},
union(2, 1),
laoda = {0,1,1}
union(1, 0)
laoda: {0, 0, 1} ==> {0,0,0}
操作上来说,如果要把a的老大改成c,不能只改a,把所有以a为老大的小弟们的老大都改成c(相当于老板跳槽,把他所有学生都带去跳槽)
public int find(int a) {
return laoda[a];
}
public void union(int a, int b) {
int alaoda = find(a);
int blaoda = find(b);
for (int i = 0; i < laoda.length; i++) { // 把a里面所有的都变成b
if (laoda[i] = a) {
laoda[i] = b;
}
}
if (alaoda == blaoda) return;
laoda[a] = b;
count--;
size[blaoda] += size[alaoda];
}
TC& SC: Find(1), Union(n)
第三种Union- Find, Weight Union Find => Tree
核心思想:
union(a,b)刚才是a加入b,or b加入a
现在一定是小的那个,加入大的那个大小: size[i]
是用tree的形式,越靠近tree的顶,越是那个大的
example
public int find(int a) {
while(a != laoda[a]) {
a = laoda[a];
}
return a;
}
public void union(int a, int b) {
int alaoda = find(a);
int blaoda = find(b);
if (alaoda == blaoda) return;
if (size[alaoda] >= size[blaoda]) {
laoda[blaoda] = alaoda // 不是laoda[b] = a;
size[alaoda] += size[blaoda];
}
else{
laoda[alaoda] = blaoda;
size[blaoda] += size[alaoda];
}
}
这里相当于size是weight
第四种Union-Find:Weighted Union FInd with PathCompression
Path Compression:路径压缩
路径压缩精髓:苟富贵勿相忘(一个人富贵了,所有帮助给你的人全都浮过)
example
//细节版本
public int find(int a) {
//自己得先富贵了,真的找到真老大
int zhenlaoda = a;
while (laoda[zhenlaoda] != zhenlaoda) {
zhenlaoda = laoda[zhenlaoda];
}
//什么都没有做,但是至少知道了真老大是谁,可以帮别人;
int 每一次帮你的人 = a;
while (每一次帮你的人 != zhenlaoda) {
每一次帮你的人 = laoda[a]; // 记录爷爷的节点是谁
laoda[a] = zhenlaoda;
a = 每一次帮你的人;
}
return zhenlaoda;
}
public void union(int a, int b) {
int alaoda = find(a);
int blaoda = find(b);
if (alaoda == blaoda) return;
if (size[alaoda] >= size[blaoda]) {
laoda[blaoda] = alaoda; //!!注意这里!!
size[alaoda] += size[blaoda];
}else {
laoda[alaoda] = blaoda;
size[blaoda] += size[alaoda];
}
}
其他版本,Pure Recursion Implementation
step1 : function 的定义
int fnd(a): 给我一个点a可以找到a所在的帮派
step 2: base case
laoda[a] = a return a
step 3: subproblem
find(laoda[a]) 这个子问题return的结果的人其实就是zhenlaoda
step 4 recursion rule
laoda[a]上面的点已经路径压缩完了
真老大不就是子问题retun的结果了么?
laoda[a] = zhenlaoda
return zhenlaoda
//pure recursion
public int find(int a) {
inf (laoda[a] == a) {
return a;
}
int zhenlaoda = find(laoda[a]);
laoda[a] = zhenlaoda;
return zhenlaoda;
}
//更简化版本
public int find(int a) {
return laoda[a] = a == laoda[a]? a: find(laoda[a]);
}
面试简化版本
class UnionFind{
int[] laoda;
public UnionFind(int n) {
this.laoda = new int[n];
for(int i = 0; i < n; i++) {
laoda[i] = i;
}
}
public int find(int a) {
return laoda[a] = a == laoda[a]? a: find(laoda[a]);
}
public void union(int a , int b) {
int alaoda = find(a);
int blaoda = find(b)l
if (alaoda = blaoda) return;
if (size[alaoda] >= size[blaoda]) {
laoda[blaoda] = alaoda;
size[alaoda] += size[blaoda];
}else {
laoda[alaoda] = blaoda;
size[blaoda] += size[alaoda];
}
}
}
多加一个getMaxSetLaoda
class UnionFind{
int[] laoda;
int[] size;
public UnionFind(int n) {
this.laoda = new int[n];
this.size = new int[n];
for(int i = 0; i < n; i++) {
laoda[i] = i;
size[i] = i;
}
}
public int find(int a) {
return laoda[a] = a == laoda[a]? a: find(laoda[a]);
}
public void union(int a , int b) {
int alaoda = find(a);
int blaoda = find(b)l
if realMaxLaoda = find(maxLaoda);
if (size[alaoda] >= size[blaoda]) {
laoda[blaoda] = alaoda;
size[alaoda] += size[blaoda];
if (size[alaoda] > size[realMaxLaoda]) {
maxLaoda = alaoda;
}
}else {
laoda[alaoda] = blaoda;
size[blaoda] += size[alaoda];
if (size[blaoda]> size[realMaxLaoda]) {
maxLaoda = blaoda;
}
}
}
public int getMAxSetLaoda() {
return maxLaoda;
}
}
add a method findMax()
add a method findMax() to the union-find data type so that findMax(i) returns the largest element in the connected component containing i.
对于UF实现上的follow up怎么做?思考徐亚激素哪些新的field,然后改变union(95%) + find(5%)更新他们
class UnionFind{
int[] laoda;
int[] size;
int[] max; //每个联通分量里面最大的
public UnionFind(int n) {
this.laoda = new int[n];
this.size = new int[n];
this.max = new int[n];
for(int i = 0; i < n; i++) {
laoda[i] = i;
size[i] = i;
max[i] = i;
}
}
public int find(int a) {
return laoda[a] = a == laoda[a]? a: find(laoda[a]);
}
public void union(int a , int b) {
int alaoda = find(a);
int blaoda = find(b)l
if (alaoda == blaoda) return;
if (size[alaoda] >= size[blaoda]) {
laoda[blaoda] = alaoda;
size[alaoda] += size[blaoda];
max[alaoda] = Math.max(max[alaoda], max[blaoda]);
}else {
laoda[alaoda] = blaoda;
size[blaoda] += size[alaoda];
max[alaoda] = Math.max(max[alaoda], max[blaoda]);
}
}
public int getMaxSetLaoda() {
return maxLaoda;
}
}
Last updated