diff --git a/notes/pics/uf-quick-union-tree.png b/notes/pics/uf-quick-union-tree.png
index 5007daa6..e9df9dd6 100644
Binary files a/notes/pics/uf-quick-union-tree.png and b/notes/pics/uf-quick-union-tree.png differ
diff --git a/notes/算法 - 并查集.md b/notes/算法 - 并查集.md
index 274f44e9..90dfcbfe 100644
--- a/notes/算法 - 并查集.md
+++ b/notes/算法 - 并查集.md
@@ -13,7 +13,7 @@
## 前言
-用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。
+并查集(Union-Find)用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。
@@ -94,7 +94,7 @@ public class QuickFindUF extends UF {
可以快速进行 union 操作,只需要修改一个节点的 id 值即可。
-但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。
+这种方法把每个连通分量看成一棵树:id[p] 保存的是 p 的父节点,根节点的 id 等于它自身。同一个连通分量的节点 id 值不一定相同,但根节点相同,因此 find 需要沿着 id 一直向上查找,直到找到根节点,开销较大。
@@ -133,16 +133,16 @@ public class QuickUnionUF extends UF {
## 加权 Quick Union
-为了解决 quick-union 的树通常会很高的问题,加权 quick-union 在 union 操作时会让较小的树连接到较大的树上面。
+为了解决 Quick Union 的树通常会很高的问题,加权 Quick Union 在 union 操作时会让节点较少的树连接到节点较多的树上面。
-理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。
+这样构造的树深度最多不超过 logN。这是因为一个节点的深度只在它所在的树连接到另一棵树上时加 1,而那棵树的节点数不少于它,合并后的树至少是原来的两倍大;N 个节点的树最多只能翻倍 logN 次。
```java
public class WeightedQuickUnionUF extends UF {
- // 保存节点的数量信息
+ // 以该节点为根的树的节点数,只有根节点的值有意义
private int[] sz;
@@ -189,6 +189,24 @@ public class WeightedQuickUnionUF extends UF {
+```java
+@Override
+public int find(int p) {
+ int root = p;
+ while (root != id[root]) {
+ root = id[root];
+ }
+ while (p != root) { // 把路径上的节点直接链接到根节点
+ int next = id[p];
+ id[p] = root;
+ p = next;
+ }
+ return root;
+}
+```
+
+这样处理后,一组 union 和 find 操作的均摊代价非常接近 1(与反阿克曼函数 α(N) 成正比,实际中 α(N) 不超过 4),但单次操作在最坏情况下仍与 logN 成正比。
+
## 比较
| 算法 | union | find |
@@ -196,7 +214,7 @@ public class WeightedQuickUnionUF extends UF {
| Quick Find | N | 1 |
| Quick Union | 树高 | 树高 |
| 加权 Quick Union | logN | logN |
-| 路径压缩的加权 Quick Union | 非常接近 1 | 非常接近 1 |
+| 路径压缩的加权 Quick Union | 均摊非常接近 1 | 均摊非常接近 1 |
## 参考资料