Union-Find
模板
// 实现一个基于hashmap 的并查集
class UnionFind {
HashMap<Integer, Integer> father = new HashMap<Integer, Integer>();
public UnionFind(HashSet<Integer> hashSet) {
// init
for (Integer now : hashSet) {
father.put(now, now);
}
}
public int find(int x) {
int parent = father.get(x);
// must be use while-loop!!!
while (parent != father.get(parent)) {
parent = father.get(parent);
}
return parent;
}
// 路径压缩
public int compressed_find(int x) {
int parent = father.get(x);
while (parent != father.get(parent)) {
parent = father.get(parent);
}
int next;
while (x != father.get(x)) {
next = father.get(x);
father.put(x, parent);
x = next;
}
return parent;
}
public void union(int x, int y) {
int fa_x = find(x);
int fa_y = find(y);
if (fa_x != fa_y)
father.put(fa_x, fa_y);
}
}
// 实现一个array的并查集
class UnionFind {
int count;
int[] table;
UnionFind(int n) {
table = new int[n];
count = n;
for(int i = 0; i < n; i++) {
table[i] = i;
}
}
public int find(int x) {
return table[x];
}
public void union(int i1, int i2) {
int f1 = table[i1];
int f2 = table[i2];
if(f1 != f2) {
for(int i = 0; i < table.length; i++) {
if(table[i] == f2) {
table[i] = f1;
}
}
count--;
}
}
public int getCount() {
return count;
}
}
Q&A
问: 面试中会碰到只有有并查集才能快速解的问题吗
答复: 有,比如http://www.lintcode.com/en/problem/number-of-islands-ii/ islands 2 应该很多同学在G家被面到过复:
听众问题
问: 为什么要用hashmap,而不用其他的东西?
答复: 也可以用数组,如果你知道数据的范围的话。
问: 这个查早怎么能是O(1) 呢? 有while循环
答复: 单词操作的复杂度不会是O(1), 但是我们有路径压缩,长度为n的路径,一旦压缩以后,每个节点找到老大哥的复杂度都是O(1), 这里的复杂度O(1)是平均操作复杂度,在一些书上称均摊复杂度或者叫做 摊还复杂度
听众问题
问: 请分析三种解法的时间空间复杂度
答复: 三种方法的时间复杂度都是O(N)的,dfs和bfs 每个节点只属于一个连通块,所以每个节点只访问一次, 每条边链接两个点,所以每条边访问两次,复杂度是O(N+M).
听众问题
问: size怎么看呢?
答复: 我们每个集合有一个老大哥A,那么你可以用一个数据结构记录 Hash[A] 表示 A作为老大哥的节点所在集合的size ,A和B所在集合合并的时候 就是Hash[新的老大哥] = Hash[A的老大哥] + Hash[B的老大哥]
问: 如果fa_a == fa_b,那么合并这两个点,还是自己指向自己吧?有必要做最后一个判断么?
答复: 如果A的老大哥 和 B的老大哥是一样的,说明他们属于同一个集合 这时候是不需要合并的
听众问题
问: 有環怎麼判斷? 沒聽清楚
答复: 如果A的老大哥和B的老大哥一样了,那么此时已经是同一个集合(同一个连通块了),那么我们就不需要再去合并了。
听众问题
问: 代码里面怎么判断A和B在一个集合, 用BFS吗
答复: A的老大哥 等于 B的老大哥 就是在同一个集合内
听众问题
问: 有环~能在说下吗?
答复: 有环说明判断的时候两个点已经在同一个连通块内,已经属于同一个集合,此时两个点的老大哥一定是一样了的,所以不需要合并了(已经在同一个集合内了)
听众问题
听众问题
问: union操作怎么实现的
答复: union操作就是合并两个老大哥,大家应该看到 老大哥是没有父节点的(他是root 节点),A是老大哥,B是老大哥,合并他们所在的结合,可以把A的father指向B,这样B成了合并后的老大哥了
问: 老师,想再请问一下这个题的follow up,如果可能海水淹了现有的岛屿,该怎么做
答复: 就是有undo操作,一些公司可能问过,按原来添加的顺序反方向撤销操作,可以用stack保存结果就可以了。如果是随意删除岛屿就不能用并查集了,因为并查集不支持这种操作。没有好的解决方案