auto commit
44
notes/算法.md
|
@ -454,7 +454,7 @@ public class WeightedQuickUnionUF {
|
||||||
|
|
||||||
<font size=4> **约定** </font><br>
|
<font size=4> **约定** </font><br>
|
||||||
|
|
||||||
待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法。
|
待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法,可以用它来判断两个元素的大小关系。
|
||||||
|
|
||||||
研究排序算法的成本模型时,计算的是比较和交换的次数。
|
研究排序算法的成本模型时,计算的是比较和交换的次数。
|
||||||
|
|
||||||
|
@ -476,7 +476,7 @@ private void exch(Comparable[] a, int i, int j){
|
||||||
|
|
||||||
找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
|
找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//222768a7-914f-4d64-b874-d98f3b926fb6.jpg" width=""/> </div><br>
|
<div align="center"> <img src="../pics//aa62b91f-3540-4a28-8ea9-045f62ab3bcc.png" width="800"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Selection {
|
public class Selection {
|
||||||
|
@ -499,7 +499,7 @@ public class Selection {
|
||||||
|
|
||||||
插入排序从左到右进行,每次都将当前元素插入到左部已经排序的数组中,使得插入之后左部数组依然有序。
|
插入排序从左到右进行,每次都将当前元素插入到左部已经排序的数组中,使得插入之后左部数组依然有序。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//065c3bbb-3ea0-4dbf-8f26-01d0e0ba7db7.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//a451b523-7e24-4fae-8e35-c46b14beed68.png" width="800"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Insertion {
|
public class Insertion {
|
||||||
|
@ -520,7 +520,7 @@ public class Insertion {
|
||||||
|
|
||||||
<font size=3> **选择排序和插入排序的比较** </font> </br>
|
<font size=3> **选择排序和插入排序的比较** </font> </br>
|
||||||
|
|
||||||
对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比是一个较小的常数。
|
对于随机排列的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比是一个较小的常数。
|
||||||
|
|
||||||
## 希尔排序
|
## 希尔排序
|
||||||
|
|
||||||
|
@ -530,7 +530,9 @@ public class Insertion {
|
||||||
|
|
||||||
希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
|
希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//8320bad6-3f91-4a15-8e3d-68e8f39649b5.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//bed9d745-f971-405d-ba57-bcfa7986c8bd.png" width="600"/> </div><br>
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//6e8f16d1-7dea-4331-aea0-3dd739db00a4.png" width="800"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Shell {
|
public class Shell {
|
||||||
|
@ -558,7 +560,9 @@ public class Shell {
|
||||||
|
|
||||||
归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。
|
归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//dcf265ad-fe35-424d-b4b7-d149cdf239f4.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//8d13ee52-e881-41eb-ad70-678c411f2718.png" width="800"/> </div><br>
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//b5fed547-a989-4ead-81d5-ea72660faf99.png" width="800"/> </div><br>
|
||||||
|
|
||||||
### 1. 归并方法
|
### 1. 归并方法
|
||||||
|
|
||||||
|
@ -587,6 +591,8 @@ public class MergeSort {
|
||||||
|
|
||||||
### 2. 自顶向下归并排序
|
### 2. 自顶向下归并排序
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//c95a18ae-4b97-4fa9-9806-d9ed7b345b42.png" width="1000"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public static void sort(Comparable[] a) {
|
public static void sort(Comparable[] a) {
|
||||||
aux = new Comparable[a.length];
|
aux = new Comparable[a.length];
|
||||||
|
@ -602,19 +608,15 @@ private static void sort(Comparable[] a, int lo, int hi) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//6468a541-3a9a-4008-82b6-03a0fe941d2a.png" width=""/> </div><br>
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//c7665f73-c52f-4ce4-aed3-592bbd76265b.png" width=""/> </div><br>
|
|
||||||
|
|
||||||
因为每次都将问题对半分成两个子问题,而这种对半分的算法复杂度一般为 O(NlogN),因此该归并排序方法的时间复杂度也为 O(NlogN)。
|
因为每次都将问题对半分成两个子问题,而这种对半分的算法复杂度一般为 O(NlogN),因此该归并排序方法的时间复杂度也为 O(NlogN)。
|
||||||
|
|
||||||
因为小数组的递归操作会过于频繁,因此使用插入排序来处理小数组将会获得更高的性能。
|
因为小数组的递归操作会过于频繁,因此可以在数组过小时切换到插入排序来提高性能。
|
||||||
|
|
||||||
### 3. 自底向上归并排序
|
### 3. 自底向上归并排序
|
||||||
|
|
||||||
先归并那些微型数组,然后成对归并得到的子数组。
|
先归并那些微型数组,然后成对归并得到的子数组。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//c7b9b4c8-83d1-4eb0-8408-ea6576a9ed90.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//4e3faf22-fa80-445e-a57d-594c37bb76e7.png" width="1000"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public static void busort(Comparable[] a) {
|
public static void busort(Comparable[] a) {
|
||||||
|
@ -634,7 +636,7 @@ public static void busort(Comparable[] a) {
|
||||||
|
|
||||||
归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
|
归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//61b4832d-71f3-413c-84b6-237e219b9fdc.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//202cd47c-ef2f-4c0e-a511-53359553387d.png" width="600"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class QuickSort {
|
public class QuickSort {
|
||||||
|
@ -654,9 +656,9 @@ public class QuickSort {
|
||||||
|
|
||||||
### 2. 切分
|
### 2. 切分
|
||||||
|
|
||||||
取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断继续这个过程,就可以保证左指针的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和左子数组最右侧的元素 a[j] 交换然后返回 j 即可。
|
取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和左子数组最右侧的元素 a[j] 交换然后返回 j 即可。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//e198c201-f386-4491-8ad6-f7e433bf992d.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//037f84c6-b470-42e0-a2d9-fd8cd7e96fa8.png" width="400"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
private static int partition(Comparable[] a, int lo, int hi) {
|
private static int partition(Comparable[] a, int lo, int hi) {
|
||||||
|
@ -677,27 +679,27 @@ private static int partition(Comparable[] a, int lo, int hi) {
|
||||||
|
|
||||||
快速排序是原地排序,不需要辅助数组,但是递归调用需要辅助栈。
|
快速排序是原地排序,不需要辅助数组,但是递归调用需要辅助栈。
|
||||||
|
|
||||||
快速排序最好的情况下是每次都正好能将数组对半分,这样递归调用次数才是最少的。这种情况下比较次数为 C<sub>N</sub>=2C<sub>N/2</sub>+N,也就是复杂度为 O(Nlog<sub>N</sub>)。
|
快速排序最好的情况下是每次都正好能将数组对半分,这样递归调用次数才是最少的。这种情况下比较次数为 C<sub>N</sub>=2C<sub>N/2</sub>+N,复杂度为 O(NlogN)。
|
||||||
|
|
||||||
最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分,如此这般。因此最坏的情况下需要比较 N<sup>2</sup>/2。为了防止数组最开始就是有序的,在进行快速排序时需要随机打乱数组。
|
最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分,如此这般。因此最坏的情况下需要比较 N<sup>2</sup>/2。为了防止数组最开始就是有序的,在进行快速排序时需要随机打乱数组。
|
||||||
|
|
||||||
### 4. 算法改进
|
### 4. 算法改进
|
||||||
|
|
||||||
#### 4.1 切换到插入排序
|
**(一)切换到插入排序**
|
||||||
|
|
||||||
因为快速排序在小数组中也会调用自己,对于小数组,插入排序比快速排序的性能更好,因此在小数组中可以切换到插入排序。
|
因为快速排序在小数组中也会调用自己,对于小数组,插入排序比快速排序的性能更好,因此在小数组中可以切换到插入排序。
|
||||||
|
|
||||||
#### 4.2 三取样
|
**(二)三取样**
|
||||||
|
|
||||||
最好的情况下是每次都能取数组的中位数作为切分元素,但是计算中位数的代价很高。人们发现取 3 个元素并将大小居中的元素作为切分元素的效果最好。
|
最好的情况下是每次都能取数组的中位数作为切分元素,但是计算中位数的代价很高。人们发现取 3 个元素并将大小居中的元素作为切分元素的效果最好。
|
||||||
|
|
||||||
#### 4.3 三向切分
|
**(三)三向切分**
|
||||||
|
|
||||||
对于有大量重复元素的数组,可以将数组切分为三部分,分别对应小于、等于和大于切分元素。
|
对于有大量重复元素的数组,可以将数组切分为三部分,分别对应小于、等于和大于切分元素。
|
||||||
|
|
||||||
三向切分快速排序对于只有若干不同主键的随机数组可以在线性时间内完成排序。
|
三向切分快速排序对于只有若干不同主键的随机数组可以在线性时间内完成排序。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//9d2226dc-c4a3-40ec-9b3e-a46bf86af499.png" width=""/> </div><br>
|
<div align="center"> <img src="../pics//b43437dd-16b6-4023-bdda-d6f7d6db762e.png" width="400"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Quick3Way {
|
public class Quick3Way {
|
||||||
|
@ -847,7 +849,7 @@ public static void sort(Comparable[] a){
|
||||||
|
|
||||||
### 1. 排序算法的比较
|
### 1. 排序算法的比较
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//be53c00b-2534-4dc6-ad03-c55995c47db9.jpg" width=""/> </div><br>
|
<div align="center"> <img src="../pics//e4ca3383-910a-4936-8ac2-9e8fd31b736b.png" width="1000"/> </div><br>
|
||||||
|
|
||||||
快速排序时最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间增长数量级为 \~cNlogN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分之后,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。
|
快速排序时最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间增长数量级为 \~cNlogN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分之后,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。
|
||||||
|
|
||||||
|
|
BIN
pics/037f84c6-b470-42e0-a2d9-fd8cd7e96fa8.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
pics/202cd47c-ef2f-4c0e-a511-53359553387d.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
pics/4e3faf22-fa80-445e-a57d-594c37bb76e7.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
pics/6e8f16d1-7dea-4331-aea0-3dd739db00a4.png
Normal file
After Width: | Height: | Size: 228 KiB |
BIN
pics/8d13ee52-e881-41eb-ad70-678c411f2718.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
pics/a451b523-7e24-4fae-8e35-c46b14beed68.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
pics/aa62b91f-3540-4a28-8ea9-045f62ab3bcc.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
pics/b43437dd-16b6-4023-bdda-d6f7d6db762e.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
pics/b5fed547-a989-4ead-81d5-ea72660faf99.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
pics/bed9d745-f971-405d-ba57-bcfa7986c8bd.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
pics/c95a18ae-4b97-4fa9-9806-d9ed7b345b42.png
Normal file
After Width: | Height: | Size: 243 KiB |
BIN
pics/e4ca3383-910a-4936-8ac2-9e8fd31b736b.png
Normal file
After Width: | Height: | Size: 218 KiB |