算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
插入排序 |
|
|
|
|
- 稳定
- 空间复杂度:O(1)
- 时间复杂度:O(n2)
- 序列完全正序:仅需比较n−1次
- 序列完全逆序:比较i=2∑n(i)次、移动i=2∑n(i+1)次
- 序列基本有序:O(n)
- 适应性:随着排序的的进行,序列变得基本有序,算法效率逐渐提高。
void directInsertSort(int arr[], int len) {
for (int i = 1; i < len; i++) {
int j = i - 1;
int v = arr[i];
while (j >= 0 && v < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = v;
}
}
输入 A, N
循环 i ∈ [1, N - 1]
j = i - 1, v = A[i]
循环当 j ≥ 0 且 v < A[j]
A[j + 1] = A[j]
j--
A[j + 1] = v
void binaryInsertSort(int arr[], int len) {
for (int i = 1; i < len; i++) {
int l = 0, m, r = i - 1, v = arr[i];
while (l <= r) {
m = (l + r) / 2;
if (arr[m] <= v) {
l = m + 1;
} else {
r = m - 1;
}
}
int j = i - 1;
while (j > r) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = v;
}
}
输入 A, N
循环 i ∈ [1, N-1]
l = 0, r = i - 1, v = A[i]
循环当 l ≤ r
m = (l + r) / 2
若 A[m] <= v 则 l = m + 1 否则 r = m - 1
j = i - 1
循环当 j > r
A[j + 1] = A[j]
j--
A[j + 1] = v
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
希尔排序 |
|
|
|
|
- 不稳定
- 空间复杂度:O(1)
- 大概时间复杂度:O(n23)(由于数学问题,无法准确描述)
- 适应性:由于希尔排序基于插入排序,希尔排序继承了插入排序的自适应性,但适应性较直接插入排序弱。
void shellInsertionSort(int arr[], int len) {
for (int step = len / 2; step > 0; step /= 2) {
for (int i = step; i < len; i++) {
int j = i,
k = j - step,
dump = arr[j];
while (k >= 0 && arr[k] > dump) {
arr[j] = arr[k];
j = k;
k = j - step;
}
arr[j] = dump;
}
}
}
在交换单位的消耗成本很高的应用中,选择排序很可能也是合适的算法选择。
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
选择排序 |
|
|
|
|
- 不稳定
- 空间复杂度:O(1)
- 时间复杂度:O(n2)
- 不适应性
void simpleSelectSort(int arr[], int len) {
for (int i = 0; i < len; i++) {
int m = i;
for (int j = i + 1; j < len; j++) {
if (arr[j] < arr[m]) m = j;
}
swapValue(arr, m, i);
}
}
输入 A, N
循环 i ∈ [0, N - 1]
m = i
循环 j ∈ [i + 1, N - 1]
若 A[j] < A[m] 则 m = j
交换 A[i], A[m] 的值
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
堆排序 |
|
|
|
|
- 不稳定
- 空间复杂度:O(1)
- 时间复杂度:O(n⋅log2n)
- 没有真正适应
void heapAdjust(int arr[], int len, int i) {
int iL = 2 * i + 1;
int iR = 2 * i + 2;
int j = i;
if (iL < len && arr[iL] > arr[j]) j = iL;
if (iR < len && arr[iR] > arr[j]) j = iR;
if (j != i) {
swapValue(arr, j, i);
heapAdjust(arr, len, j);
}
}
void heapSelectSort(int arr[], int len) {
for (int i = len / 2 - 1; i >= 0; i--) heapAdjust(arr, len, i);
for (int i = len - 1; i >= 1; i--) {
swapValue(arr, i, 0);
heapAdjust(arr, i, 0);
}
}
输入 A, N
循环 i ∈ [N/2 - 1, 0]
heapAdjust(A, N, i)
循环 i ∈ [N - 1, 1]
交换 A[i], A[0] 的值
heapAdjust(A, i, 0)
heapAdjust:
输入 A, N, I
l = 2 × I + 1
r = 2 × I + 2
j = I
若 l < N 且 A[l] > A[j] 则 j = l
若 r < N 且 A[r] > A[j] 则 j = r
若 j ≠ I 则
交换 A[j], A[I] 的值
heapAdjust(A, N, j)
- 左子序=父序×2+1
- 右子序=父序×2+2
- 非叶序区间:[0,L/2−1]
- 叶子序区间:[L/2,L−1]
int A[] = {?, ?, ?, ?, ?, ?, ?, ?};
graph TB
style A0 fill:#CC9933;
style A1 fill:#CC9933;
style A2 fill:#CC9933;
style A3 fill:#CC9933;
style A4 fill:#99FF99;
style A5 fill:#99FF99;
style A6 fill:#99FF99;
style A7 fill:#99FF99;
style NIL_A3_R color:white,stroke:#aaa,fill:#aaa;
style NIL_A4_L color:white,stroke:#aaa,fill:#aaa;
style NIL_A4_R color:white,stroke:#aaa,fill:#aaa;
style NIL_A5_L color:white,stroke:#aaa,fill:#aaa;
style NIL_A5_R color:white,stroke:#aaa,fill:#aaa;
style NIL_A6_L color:white,stroke:#aaa,fill:#aaa;
style NIL_A6_R color:white,stroke:#aaa,fill:#aaa;
A0(("A[0]"));
A1(("A[1]"));
A2(("A[2]"));
A3(("A[3]"));
A4(("A[4]"));
A5(("A[5]"));
A6(("A[6]"));
A7(("A[7]"));
A0---A1; A0---A2;
A1---A3; A1---A4;
A2---A5; A2---A6;
A3---A7; A3---NIL_A3_R(("NIL"));
A4---NIL_A4_L(("NIL")); A4---NIL_A4_R(("NIL"));
A5---NIL_A5_L(("NIL")); A5---NIL_A5_R(("NIL"));
A6---NIL_A6_L(("NIL")); A6---NIL_A6_R(("NIL"));
int A[] = {49, 38, 13, 49, 76, 65, 27, 97};
int R[] = {97, 76, 65, 49, 49, 13, 27, 38};
- 当前调整节点、被传递调整的节点、栈顶最值、取代栈顶的元素、冻结
graph TD
subgraph 2
style 2 stroke:#333,stroke-width:2px,fill:grey;
style B2 fill:yellow;
style D2 fill:orange;
A2((49));
B2((38));
C2((65));
D2((97));
E2((76));
F2((13));
G2((27));
H2((49));
A2---B2; A2---C2;
B2-.-D2; B2---E2;
C2---F2; C2---G2;
D2-.-H2;
end
subgraph 1
style 1 stroke:#333,stroke-width:2px,fill:grey;
style C1 fill:yellow;
A1((49));
B1((38));
C1((13));
D1((97));
E1((76));
F1((65));
G1((27));
H1((49));
A1---B1; A1---C1;
B1---D1; B1---E1;
C1-.-F1; C1---G1;
D1---H1;
end
subgraph 0
style 0 stroke:#333,stroke-width:2px,fill:grey;
style D0 fill:yellow;
A0((49));
B0((38));
C0((13));
D0((49));
E0((76));
F0((65));
G0((27));
H0((97));
A0---B0; A0---C0;
B0---D0; B0---E0;
C0---F0; C0---G0;
D0-.-H0;
end
graph TD
subgraph 5
style 5 stroke:#333,stroke-width:2px,fill:grey;
style H5 fill:red;
style A5 fill:yellow;
A5((38));
B5((76));
C5((65));
D5((49));
E5((49));
F5((13));
G5((27));
H5((97));
A5-.-B5; A5---C5;
B5-.-D5; B5---E5;
C5---F5; C5---G5;
D5---H5;
end
subgraph 4
style 4 stroke:#333,stroke-width:2px,fill:grey;
style A4 fill:pink;
style H4 fill:lightgreen;
A4((97));
B4((76));
C4((65));
D4((49));
E4((49));
F4((13));
G4((27));
H4((38));
A4---B4; A4---C4;
B4---D4; B4---E4;
C4---F4; C4---G4;
D4---H4;
end
subgraph 3
style 3 stroke:#333,stroke-width:2px,fill:grey;
style A3 fill:yellow;
style B3 fill:orange;
A3((49));
B3((97));
C3((65));
D3((49));
E3((76));
F3((13));
G3((27));
H3((38));
A3-.-B3; A3---C3;
B3---D3; B3-.-E3;
C3---F3; C3---G3;
D3---H3;
end
- 0-4为构建大顶堆(需要从最后一个非叶结点到根结点进行
HeapAdjust
);
- 4为构建大顶堆的结果;
- 从4开始为选择最大值排序,后续每次仅需对根节点进行
HeapAdjust
即可。
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
冒泡排序 |
|
|
|
|
- 稳定
- 空间复杂度:O(1)
- 时间复杂的:O(n2)
- 适应性
void bubbleSwapSort(int arr[], int len) {
for (int i = 0; i < len; i++) {
for (int j = len - 1; j > i; j--) {
if (arr[j - 1] > arr[j]) {
swapValue(arr, j - 1, j);
}
}
}
}
输入 A, N
循环 i ∈ [0, N - 1]
循环 j ∈ [N - 1, i)
若 A[j - 1] > A[j] 则 交换 A[j - 1], A[j] 的值
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小。
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
快速排序 |
|
|
|
|
- 不稳定
- 空间复杂度:O(log2n)
- 时间复杂度:O(n2),但通常O(n⋅log2n)
- 没有真正适应
static void arrayAdjustL(int arr[], int boundL, int boundR) {
int l = boundL;
int r = boundR;
int v = arr[l];
while (l < r) {
while (l < r && arr[r] >= v) r--;
arr[l] = arr[r];
while (l < r && arr[l] <= v) l++;
arr[r] = arr[l];
}
arr[l] = v;
if (boundL < (l - 1)) arrayAdjustL(arr, boundL, l - 1);
if ((l + 1) < boundR) arrayAdjustL(arr, l + 1, boundR);
}
void quickSwapSort(int arr[], int len) {
if (len > 1) arrayAdjustL(arr, 0, len - 1);
}
输入 A, N
若 N > 1 则 arrayAdjust(A, 0, N - 1)
arrayAdjust:
输入 A, L, R
l = L, r = R, v = A[l]
循环当 l < r
循环当 l < r 且 A[r] ≥ A[p]
r--
A[l] = A[r]
循环当 l < r 且 A[l] ≤ A[p]
l++
A[r] = A[l]
A[l] = v
若 L < (l - 1) 则 arrayAdjust(A, L, l - 1)
若 (l + 1) < R 则 arrayAdjust(A, l + 1, R)
- left、right、pivot、临时锁定、最终命中、锁定
graph LR
subgraph 6
style 6 stroke:#333,fill:grey;
style D6 fill:red;
A6((5))---
B6((2))---
C6((4))---
D6((7))---
E6((9))---
F6((8));
end
subgraph 5
style 5 stroke:#333,fill:grey;
style F5 fill:skyblue;
style D5 fill:lightgreen;
A5((5))---
B5((2))---
C5((4))---
D5((8))---
E5((9))---
F5((7));
end
subgraph 4
style 4 stroke:#333,fill:grey;
style F4 fill:skyblue;
style C4 fill:yellow;
style D4 fill:orange;
A4((5))---
B4((2))---
C4((4))---
D4((8))---
E4((9))---
F4((7));
end
subgraph 3
style 3 stroke:#333,fill:grey;
style F3 fill:skyblue;
style B3 fill:yellow;
style D3 fill:orange;
A3((5))---
B3((2))---
C3((4))---
D3((8))---
E3((9))---
F3((7));
end
subgraph 2
style 2 stroke:#333,fill:grey;
style F2 fill:skyblue;
style B2 fill:yellow,stroke:hotpink,stroke-width:5px;
style D2 fill:orange,stroke:hotpink,stroke-width:5px;
A2((5))---
B2((8))---
C2((4))---
D2((2))---
E2((9))---
F2((7));
end
subgraph 1
style 1 stroke:#333,fill:grey;
style F1 fill:skyblue;
style B1 fill:yellow,stroke:hotpink,stroke-width:5px;
style E1 fill:orange;
A1((5))---
B1((8))---
C1((4))---
D1((2))---
E1((9))---
F1((7));
end
subgraph 0
style 0 stroke:#333,fill:grey;
style F0 fill:skyblue;
style A0 fill:yellow;
style E0 fill:orange;
A0((5))---
B0((8))---
C0((4))---
D0((2))---
E0((9))---
F0((7));
end
- 注意在5中,如果pivot是大于最终命中值的话是不需要交换的,此时锁定的是其原始所在位置。
- left、right、pivot、临时锁定、最终命中、锁定
graph LR
subgraph 5
style 5 stroke:#333,fill:grey;
style C5 fill:red;
A5((2))---
B5((4))---
C5((5))---
D5((8))---
E5((9))---
F5((7));
end
subgraph 4
style 4 stroke:#333,fill:grey;
style C4 fill:lightgreen;
A4((2))---
B4((4))---
C4((?))---
D4((8))---
E4((9))---
F4((7));
end
subgraph 3
style 3 stroke:#333,fill:grey;
style B3 fill:yellow;
style C3 fill:orange,stroke:hotpink,stroke-width:5px;
A3((2))---
B3((?))---
C3((4))---
D3((8))---
E3((9))---
F3((7));
end
subgraph 2
style 2 stroke:#333,fill:grey;
style B2 fill:yellow,stroke:hotpink,stroke-width:5px;
style D2 fill:orange;
A2((2))---
B2((8))---
C2((4))---
D2((?))---
E2((9))---
F2((7));
end
subgraph 1
style 1 stroke:#333,fill:grey;
style A1 fill:yellow;
style D1 fill:orange,stroke:hotpink,stroke-width:5px;
A1((?))---
B1((8))---
C1((4))---
D1((2))---
E1((9))---
F1((7));
end
subgraph 0
style 0 stroke:#333,fill:grey;
style A0 fill:yellow,stroke:skyblue,stroke-width:5px;
style F0 fill:orange;
A0((5))---
B0((8))---
C0((4))---
D0((2))---
E0((9))---
F0((7));
end
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
快速排序三平均 |
|
|
|
|
- 不稳定
- 空间复杂度:O(log2n)
- 时间复杂度:O(n2),但通常O(n⋅log2n)
- 自适应:当有O(1)个Unique Keys时需要O(n)时间
算法 |
完全随机 |
基本有序 |
逆排序 |
少有不同 |
归并排序 |
|
|
|
|
- 稳定性
- 数组要O(n)额外空间;链表要O(log2n)额外空间
- O(n⋅log2n)时间
- 不自适应
- 不需要随机接入数据
略