auto commit

This commit is contained in:
CyC2018 2018-03-31 15:11:25 +08:00
parent 480ce3db3c
commit 9ee0daf60a
6 changed files with 52 additions and 22 deletions

View File

@ -28,6 +28,7 @@
* [红黑二叉查找树](#红黑二叉查找树)
* [散列表](#散列表)
* [应用](#应用)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
@ -41,7 +42,7 @@ N<sup>3</sup>/6-N<sup>2</sup>/2+N/3 \~ N<sup>3</sup>/6。使用 \~f(N) 来表示
### 2. 增长数量级
N<sup>3</sup>/6-N<sup>2</sup>/2+N/3 的增长数量级为 O(N<sup>3</sup>)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N<sup>3</sup>)与它是否用 Java 实现,是否运行于特定计算机上无关。
N<sup>3</sup>/6-N<sup>2</sup>/2+N/3 的增长数量级为 O(N<sup>3</sup>)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N<sup>3</sup>) 与它是否用 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) = N<sup>3</sup>/6 - N<sup>2</sup>/2 + N/3因此它的近似执行次数为 \~N<sup>3</sup>/6增长数量级为 N<sup>3</sup>
该算法的内循环为 if(a[i]+a[j]+a[k]==0) 语句,总共执行的次数为 N(N-1)(N-2) = N<sup>3</sup>/6-N<sup>2</sup>/2+N/3因此它的近似执行次数为 \~N<sup>3</sup>/6增长数量级为 N<sup>3</sup>
<font size=4> **改进** </font></br>
@ -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 算法,近似时间为 \~N<sup>3</sup>/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<Item> {
用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。
<div align="center"> <img src="../pics//7e9d0ef2-acd8-44c0-a76b-f0d2f5e76738.png" width="300"/> </div><br>
<div align="center"> <img src="../pics//dc752c5b-bb59-4616-bf9c-21276690a24d.png"/> </div><br>
<font size=4> **API** </font> <br>
<div align="center"> <img src="../pics//867abc3c-8403-43ef-8847-c1fea32996a5.png" width="800"/> </div><br>
| 方法 | 描述 |
| ---: | :--- |
| 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 节点是否连通 |
<font size=4> **基本数据结构** </font> <br>
```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 值。
<div align="center"> <img src="../pics//0120d24f-58a2-4848-afff-ce2b76d38ffc.png"/> </div><br>
```java
public int find(int p) {
@ -354,9 +373,11 @@ public class UF {
## quick-union
在 union 时只将节点的 id 值指向另一个节点 id 值,不直接用 id 来存储所属的连通分量。这样就构成一个倒置的树形结构,应该注意的是根节点需要指向自己。查找一个节点所属的连通分量时,要一直向上查找直到根节点,并使用根节点的 id 值作为本连通分量的 id 值
可以快速进行 union 操作,只需要修改一个节点的 id 值即可
<div align="center"> <img src="../pics//ae1f3f27-cb47-436d-b8a2-185618851b57.png" width="600"/> </div><br>
但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同id 值只是用来指向另一个节点。因此需要一段进行查找操作,直到找到最上层的节点。
<div align="center"> <img src="../pics//d645d71f-007e-4228-8311-6b1fb61fb232.png"/> </div><br>
```java
public int find(int p) {
@ -374,7 +395,7 @@ public class UF {
这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为触点的数目。
<div align="center"> <img src="../pics//dcbd1473-96c3-4ace-8b69-2c9342615e7e.png" width="300"/> </div><br>
<div align="center"> <img src="../pics//bfbb11e2-d208-4efa-b97b-24cd40467cd8.png"/> </div><br>
## 加权 quick-union
@ -382,7 +403,7 @@ public class UF {
理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。
<div align="center"> <img src="../pics//7f9a9342-1491-436d-bdd2-0fa94eb0e4f1.png" width="500"/> </div><br>
<div align="center"> <img src="../pics//095720ee-84b3-42ff-af71-70ceb6a2f4a3.png"/> </div><br>
```java
public class WeightedQuickUnionUF {
@ -429,7 +450,12 @@ public class WeightedQuickUnionUF {
## 各种 union-find 算法的比较
<div align="center"> <img src="../pics//cae894a9-2424-4de4-ab41-c15d7054a5e7.png" width="800"/> </div><br>
| 算法 | 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB