Dynamic connectivity (动态连通性)
We assume "is connected to" is an equivalence relation
- 自反性 Reflexive:
p is connected to p.
- 对称性 Symmetric:
if p is connected to q, then q is connected to p.
- 传递性 Transitive:
if p is connected to q and q is connected to r, then p is connected to r.
Quick Find (eager algorithm)
public class QuickFindUF
{
private int[] id;
public QuickFindUF(int N)
{
id = new int[N];
for (int i = 0; i < N; i++) {
id[i] = i;
}
}
public boolean connected(int p, int q)
{
return (id[p] == id[q]);
}
public void union(int p, int q)
{
int p_root = id[p];
int q_root = id[q];
for (int i = 0; i < id.length; i++) {
if (id[i] == p_root){
id[p] = q_root;
}
}
}
}
Takes N^2 array accesses to process sequence of N union commands on N objects.(quadratic time is much to slow)
Quick Union (lazy approach)
package com.algorithm.week1;
public class QuickUnionUF
{
private int[] id;
public QuickUnionUF(int N)
{
id = new int[N];
// 设置每个对象的id为自己的初始索引(n次数组访问)
for (int i = 0; i < N; i++) {
id[i] = i;
}
}
private int root(int i)
{
// id[i] 是 i 的父节点,不断获取父节点直到达到根节点(获取的父节点不再变化)
// 数组的访问次数取决于 i 的父节点的深度
// 7
// / \
// 3 4
// / \ \
// 5 6 1
// 例如上图 i 为 5 时父节点深度为 3
// 过程如下
// i: 5 id[5]: 3 => i: 3 id[3]: 7 => i: 7 id[7]: 7 => return 7
while (i != id[i]){
i = id[i];
}
return i;
}
public boolean connected(int p, int q)
{
// 检查 p 和 q 两者的根节点是否一致
// 数组的访问次数取决于 p 和 q 父节点的深度
return root(p) == root(q);
}
public void union(int p, int q)
{
// 数组的访问次数取决于 p 和 q 父节点的深度
int p_root = root(p);
int q_root = root(q);
// 将p的根节点指向q的根节点(p和q)
id[p_root] = q_root;
}
}
algorithm | initialize | union | find |
---|---|---|---|
quick-find | N | N | 1 |
quick-union (worst case) | N | N† († includes cost of finding roots) | N |
Quick-find 缺点
- Union 动作耗费较大(N次)
- Trees 扁平,但是维持扁平的代价太大(需要for循环遍历更新每个元素更新父节点)
Quick-union 缺点
- Trees 可以变得相当高
- Find 代价太大(能够达到N次)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。