auto commit
This commit is contained in:
parent
9fd44aa96d
commit
a1438441fd
41
notes/算法.md
41
notes/算法.md
|
@ -75,13 +75,13 @@ 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,增长数量级为 O(N<sup>3</sup>)。
|
||||
|
||||
<font size=4> **改进** </font></br>
|
||||
|
||||
通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。
|
||||
|
||||
该方法可以将 ThreeSum 算法增长数量级降低为 N<sup>2</sup>logN。
|
||||
该方法可以将 ThreeSum 算法增长数量级降低为 O(N<sup>2</sup>logN)。
|
||||
|
||||
```java
|
||||
public class ThreeSumFast {
|
||||
|
@ -399,7 +399,7 @@ public void union(int p, int q) {
|
|||
|
||||
理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。
|
||||
|
||||
<div align="center"> <img src="../pics//8229e8e7-a183-4d29-94e6-e8d8537c6ce5.png" width="200"/> </div><br>
|
||||
<div align="center"> <img src="../pics//a4c17d43-fa5e-4935-b74e-147e7f7e782c.png" width="200"/> </div><br>
|
||||
|
||||
```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 |
|
||||
|
||||
# 四、排序
|
||||
|
||||
<font size=4> **约定** </font><br>
|
||||
|
||||
待排序的元素需要实现 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){
|
|||
|
||||
找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
|
||||
|
||||
<div align="center"> <img src="../pics//ae3fc93a-44d5-4beb-b05a-874bd9c0a657.png" width="200"/> </div><br>
|
||||
<div align="center"> <img src="../pics//ed7b96ac-6428-4bd5-9986-674c54c2a959.png" width="200"/> </div><br>
|
||||
|
||||
```java
|
||||
public class Selection {
|
||||
|
@ -517,7 +515,11 @@ public class Insertion {
|
|||
}
|
||||
```
|
||||
|
||||
插入排序的复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么插入排序会很快。平均情况下插入排序需要 \~N<sup>2</sup>/4 比较以及 \~N<sup>2</sup>/4 次交换,最坏的情况下需要 \~N<sup>2</sup>/2 比较以及 \~N<sup>2</sup>/2 次交换,最坏的情况是数组是逆序的;而最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。
|
||||
插入排序的复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么插入排序会很快。
|
||||
|
||||
- 平均情况下插入排序需要 \~N<sup>2</sup>/4 比较以及 \~N<sup>2</sup>/4 次交换;
|
||||
- 最坏的情况下需要 \~N<sup>2</sup>/2 比较以及 \~N<sup>2</sup>/2 次交换,最坏的情况是数组是逆序的;
|
||||
- 最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。
|
||||
|
||||
插入排序对于部分有序数组和小规模数组特别高效。
|
||||
|
||||
|
@ -529,7 +531,7 @@ public class Insertion {
|
|||
|
||||
希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
|
||||
|
||||
<div align="center"> <img src="../pics//4bb7ed45-ec14-4d31-9da4-94024d9d3b05.png" width="500"/> </div><br>
|
||||
<div align="center"> <img src="../pics//cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png" width="500"/> </div><br>
|
||||
|
||||
```java
|
||||
public class Shell {
|
||||
|
@ -628,7 +630,8 @@ public static void busort(Comparable[] a) {
|
|||
|
||||
### 1. 基本算法
|
||||
|
||||
归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
|
||||
- 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;
|
||||
- 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
|
||||
|
||||
<div align="center"> <img src="../pics//ab77240d-7338-4547-9183-00215e7220ec.png" width="500"/> </div><br>
|
||||
|
||||
|
@ -652,7 +655,7 @@ public class QuickSort {
|
|||
|
||||
取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和 a[j] 交换位置。
|
||||
|
||||
<div align="center"> <img src="../pics//8af348d0-4d72-4f76-b56c-0a440ed4673d.png" width="400"/> </div><br>
|
||||
<div align="center"> <img src="../pics//5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png" width="400"/> </div><br>
|
||||
|
||||
```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 的位置,是为了更清晰地描述节点的位置关系。
|
||||
|
||||
<div align="center"> <img src="../pics//f3080f83-6239-459b-8e9c-03b6641f7815.png" width="200"/> </div><br>
|
||||
|
||||
|
@ -813,13 +816,13 @@ public Key delMax() {
|
|||
|
||||
由于堆可以很容易得到最大的元素并删除它,不断地进行这种操作可以得到一个递减序列。如果把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列。因此很容易使用堆来进行排序,并且堆排序是原地排序,不占用额外空间。
|
||||
|
||||
**构建堆**
|
||||
(一)构建堆
|
||||
|
||||
无序数组建立堆最直接的方法是从左到右遍历数组,然后进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,因此可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。
|
||||
|
||||
<div align="center"> <img src="../pics//b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png" width="300"/> </div><br>
|
||||
|
||||
**交换堆顶元素与最后一个元素**
|
||||
(二)交换堆顶元素与最后一个元素
|
||||
|
||||
交换之后需要进行下沉操作维持堆的有序状态。
|
||||
|
||||
|
|
BIN
pics/5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png
Normal file
BIN
pics/5aac64d3-2c7b-4f32-9e9a-1df2186f588b.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png
Normal file
BIN
pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
BIN
pics/cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png
Normal file
BIN
pics/cdbe1d12-5ad9-4acb-a717-bbc822c2acf3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
BIN
pics/ed7b96ac-6428-4bd5-9986-674c54c2a959.png
Normal file
BIN
pics/ed7b96ac-6428-4bd5-9986-674c54c2a959.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
Loading…
Reference in New Issue
Block a user