diff --git a/notes/算法.md b/notes/算法.md index 1830732f..488beede 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -75,13 +75,13 @@ 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,增长数量级为 O(N3)。 **改进**
通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。 -该方法可以将 ThreeSum 算法增长数量级降低为 N2logN。 +该方法可以将 ThreeSum 算法增长数量级降低为 O(N2logN)。 ```java public class ThreeSumFast { @@ -399,7 +399,7 @@ public void union(int p, int q) { 理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 -

+

```java public class WeightedQuickUnionUF { @@ -450,13 +450,11 @@ public class WeightedQuickUnionUF { | :---: | :---: | :---: | | quick-find | N | 1 | | quick-union | 树高 | 树高 | -| 加权 quick-union | lgN | lgN | +| 加权 quick-union | logN | logN | | 路径压缩的加权 quick-union | 非常接近 1 | 非常接近 1 | # 四、排序 - **约定**
- 待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法,可以用它来判断两个元素的大小关系。 研究排序算法的成本模型时,计算的是比较和交换的次数。 @@ -464,11 +462,11 @@ public class WeightedQuickUnionUF { 使用辅助函数 less() 和 exch() 来进行比较和交换的操作,使得代码的可读性和可移植性更好。 ```java -private boolean less(Comparable v, Comparable w){ +private boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } -private void exch(Comparable[] a, int i, int j){ +private void exch(Comparable[] a, int i, int j) { Comparable t = a[i]; a[i] = a[j]; a[j] = t; @@ -479,7 +477,7 @@ private void exch(Comparable[] a, int i, int j){ 找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 -

+

```java public class Selection { @@ -517,7 +515,11 @@ public class Insertion { } ``` -插入排序的复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么插入排序会很快。平均情况下插入排序需要 \~N2/4 比较以及 \~N2/4 次交换,最坏的情况下需要 \~N2/2 比较以及 \~N2/2 次交换,最坏的情况是数组是逆序的;而最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。 +插入排序的复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么插入排序会很快。 + +- 平均情况下插入排序需要 \~N2/4 比较以及 \~N2/4 次交换; +- 最坏的情况下需要 \~N2/2 比较以及 \~N2/2 次交换,最坏的情况是数组是逆序的; +- 最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。 插入排序对于部分有序数组和小规模数组特别高效。 @@ -529,7 +531,7 @@ public class Insertion { 希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。 -

+

```java public class Shell { @@ -628,7 +630,8 @@ public static void busort(Comparable[] a) { ### 1. 基本算法 -归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。 +- 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序; +- 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。

@@ -652,7 +655,7 @@ public class QuickSort { 取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和 a[j] 交换位置。 -

+

```java private static int partition(Comparable[] a, int lo, int hi) { @@ -679,15 +682,15 @@ private static int partition(Comparable[] a, int lo, int hi) { ### 4. 算法改进 -**(一)切换到插入排序** +(一)切换到插入排序 因为快速排序在小数组中也会调用自己,对于小数组,插入排序比快速排序的性能更好,因此在小数组中可以切换到插入排序。 -**(二)三取样** +(二)三取样 最好的情况下是每次都能取数组的中位数作为切分元素,但是计算中位数的代价很高。人们发现取 3 个元素并将大小居中的元素作为切分元素的效果最好。 -**(三)三向切分** +(三)三向切分 对于有大量重复元素的数组,可以将数组切分为三部分,分别对应小于、等于和大于切分元素。 @@ -720,7 +723,7 @@ public class Quick3Way { 堆的某个节点的值总是大于等于子节点的值,并且堆是一颗完全二叉树。 -堆可以用数组来表示,因为堆是一种完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里我们不使用数组索引为 0 的位置,是为了更清晰地理解节点的关系。 +堆可以用数组来表示,因为堆是一种完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里我们不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。

@@ -813,13 +816,13 @@ public Key delMax() { 由于堆可以很容易得到最大的元素并删除它,不断地进行这种操作可以得到一个递减序列。如果把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列。因此很容易使用堆来进行排序,并且堆排序是原地排序,不占用额外空间。 -**构建堆** +(一)构建堆 无序数组建立堆最直接的方法是从左到右遍历数组,然后进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,因此可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。

-**交换堆顶元素与最后一个元素** +(二)交换堆顶元素与最后一个元素 交换之后需要进行下沉操作维持堆的有序状态。 diff --git a/pics/5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png b/pics/5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png new file mode 100644 index 00000000..f457a99d Binary files /dev/null and b/pics/5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png differ diff --git a/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png b/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png new file mode 100644 index 00000000..461fffb0 Binary files /dev/null and b/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png differ diff --git a/pics/cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png b/pics/cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png new file mode 100644 index 00000000..63792c74 Binary files /dev/null and b/pics/cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png differ diff --git a/pics/ed7b96ac-6428-4bd5-9986-674c54c2a959.png b/pics/ed7b96ac-6428-4bd5-9986-674c54c2a959.png new file mode 100644 index 00000000..78b3cace Binary files /dev/null and b/pics/ed7b96ac-6428-4bd5-9986-674c54c2a959.png differ