diff --git a/notes/算法.md b/notes/算法.md index 97c61324..b0d03d7a 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -28,6 +28,7 @@ * [红黑二叉查找树](#红黑二叉查找树) * [散列表](#散列表) * [应用](#应用) +* [参考资料](#参考资料) @@ -41,7 +42,7 @@ N3/6-N2/2+N/3 \~ N3/6。使用 \~f(N) 来表示 ### 2. 增长数量级 -N3/6-N2/2+N/3 的增长数量级为 O(N3)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N3)与它是否用 Java 实现,是否运行于特定计算机上无关。 +N3/6-N2/2+N/3 的增长数量级为 O(N3)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N3) 与它是否用 Java 实现,是否运行于特定计算机上无关。 ### 3. 内循环 @@ -57,13 +58,13 @@ ThreeSum 用于统计一个数组中三元组的和为 0 的数量。 ```java public class ThreeSum { - public static int count(int[] a) { - int N = a.length; + public int count(int[] nums) { + int N = nums.length; int cnt = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { for (int k = j + 1; k < N; k++) { - if (a[i] + a[j] + a[k] == 0) { + if (nums[i] + nums[j] + nums[k] == 0) { cnt++; } } @@ -74,7 +75,7 @@ public class ThreeSum { } ``` -该算法的内循环为 if (a[i] + a[j] + a[k] == 0) 语句,总共执行的次数为 N(N-1)(N-2) = N3/6 - N2/2 + N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3。 +该算法的内循环为 if(a[i]+a[j]+a[k]==0) 语句,总共执行的次数为 N(N-1)(N-2) = N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3 **改进**
@@ -84,21 +85,31 @@ public class ThreeSum { ```java public class ThreeSumFast { - public static int count(int[] a) { - Arrays.sort(a); - int N = a.length; + public int count(int[] nums) { + Arrays.sort(nums); + int N = nums.length; int cnt = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { - // rank() 方法返回元素在数组中的下标,如果元素不存在,这里会返回 -1。 // 应该注意这里的下标必须大于 j,否则会重复统计。 - if (BinarySearch.rank(-a[i] - a[j], a) > j) { + if (binarySearch(nums, -nums[i] - nums[j]) > j) { cnt++; - } + } } } return cnt; } + + private int binarySearch(int[] nums, int target) { + int l = 0, h = nums.length - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (nums[m] == target) return m; + else if (nums[m] < target) h = m - 1; + else l = m + 1; + } + return -1; + } } ``` @@ -109,7 +120,7 @@ public class ThreeSumFast { 例如对于暴力方法的 ThreeSum 算法,近似时间为 \~N3/6。进行如下实验:多次运行该算法,每次取的 N 值为前一次的两倍,统计每次执行的时间,并统计本次运行时间与前一次运行时间的比值,得到如下结果: | N | Time | Ratio | -| --- | --- | --- | +| :---: | :---: | :---: | | 250 | 0.0 | 2.7 | | 500 | 0.0 | 4.8 | | 1000 | 0.1 | 6.9 | @@ -305,17 +316,21 @@ public class Queue { 用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。 -

+

**API**
-

+| 方法 | 描述 | +| ---: | :--- | +| UF(int N) | 构造一个大小为 N 的并查集 | +| void union(int p, int q) | 连接 p 和 q 节点 | +| int find(int p) | 查找 p 所在的连通分量 | +| boolean connected(int p, int q) | 判断 p 和 q 节点是否连通 | **基本数据结构**
```java public class UF { - // 使用 id 数组来保存点的连通信息 private int[] id; public UF(int N) { @@ -333,9 +348,13 @@ public class UF { ## quick-find -保证在同一连通分量的所有节点的 id 值相等。 +可以快速进行 find 操作,即可以快速判断两个节点是否连通。 -这种方法可以快速取得一个节点的 id 值,并且判断两个节点是否连通。但是 union 的操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 +同一连通分量的所有节点的 id 值相等。 + +但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 + +

```java public int find(int p) { @@ -354,9 +373,11 @@ public class UF { ## quick-union -在 union 时只将节点的 id 值指向另一个节点 id 值,不直接用 id 来存储所属的连通分量。这样就构成一个倒置的树形结构,应该注意的是根节点需要指向自己。查找一个节点所属的连通分量时,要一直向上查找直到根节点,并使用根节点的 id 值作为本连通分量的 id 值。 +可以快速进行 union 操作,只需要修改一个节点的 id 值即可。 -

+但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一段进行查找操作,直到找到最上层的节点。 + +

```java public int find(int p) { @@ -374,7 +395,7 @@ public class UF { 这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为触点的数目。 -

+

## 加权 quick-union @@ -382,7 +403,7 @@ public class UF { 理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 -

+

```java public class WeightedQuickUnionUF { @@ -429,7 +450,12 @@ public class WeightedQuickUnionUF { ## 各种 union-find 算法的比较 -

+| 算法 | union | find | +| :---: | :---: | :---: | +| quick-find | N | 1 | +| quick-union | 树高 | 树高 | +| 加权 quick-union | lgN | lgN | +| 路径压缩的加权 quick-union | 非常接近 1 | 非常接近 1 | # 四、排序 @@ -1596,3 +1622,7 @@ public class SparseVector { } ``` +# 参考资料 + +- Sedgewick R. Algorithms[M]. Pearson Education India, 1988. + diff --git a/pics/0120d24f-58a2-4848-afff-ce2b76d38ffc.png b/pics/0120d24f-58a2-4848-afff-ce2b76d38ffc.png new file mode 100644 index 00000000..7b5db630 Binary files /dev/null and b/pics/0120d24f-58a2-4848-afff-ce2b76d38ffc.png differ diff --git a/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png b/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png new file mode 100644 index 00000000..57e4309b Binary files /dev/null and b/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png differ diff --git a/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png b/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png new file mode 100644 index 00000000..b9d9dba2 Binary files /dev/null and b/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png differ diff --git a/pics/d645d71f-007e-4228-8311-6b1fb61fb232.png b/pics/d645d71f-007e-4228-8311-6b1fb61fb232.png new file mode 100644 index 00000000..912476d1 Binary files /dev/null and b/pics/d645d71f-007e-4228-8311-6b1fb61fb232.png differ diff --git a/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png b/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png new file mode 100644 index 00000000..66e77b6d Binary files /dev/null and b/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png differ