Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

快速排序

简介

复杂度分析: 期望复杂度 O(n lg n) 最坏复杂度 O(n^2)
数组 A[p.. r] 被划分为两个(可能为空)子数组 A[p.. q-1] A[q+ 1.. r], 使得
A[p .. l] 中的每一个元素都小于等于 A[q], A[q] 也小于等于 A[q+l..r] 中的每个元素。
其中,计算下标 也是划分过程的一部分。

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 复杂度分析: 期望复杂度 O(n lg n) 最坏复杂度 O(n^2)
// 数组 A[p.. r] 被划分为两个(可能为空)子数组 A[p.. q-1] A[q+ 1.. r], 使得
// A[p .. l] 中的每一个元素都小于等于 A[q], A[q] 也小于等于 A[q+l..r] 中的每个元素。
// 其中,计算下标 也是划分过程的一部分。

int partition(int *A, int p, int r) {
int x = A[r]; // 找到末尾的数字
int i = p - 1;// 将指针指向起始位置前一位
for (int j = p; j <= r - 1; j++) {// 遍历[p,r),
if (A[j] <= x) { // 找到当前位置数字比末尾数字小
++i;// 将数组中i++位置数字和j位置交换;
std::swap(A[i], A[j]);
}
}
std::swap(A[i + 1], A[r]); // 将最后一个被交换的对象和r位置的交换
return i + 1;
}

void quick_sort(int *A, int p, int r) {
if (p < r) {
int q = partition(A, p, r);
quick_sort(A, p, q - 1);
quick_sort(A, q + 1, r);
}
}

int main(int argn, char *argc[]) {
srand((unsigned)time(0));
const int MAX_ARRAY = 10;
const int NUMBER_RANGE = 500;
int *A = new int[MAX_ARRAY];
for (int i = 0; i < MAX_ARRAY; i++) {
A[i] = rand() % NUMBER_RANGE;
}
for (int i = 0; i < MAX_ARRAY; i++) {
std::cout << A[i] << " ";
}
std::cout << std::endl << "--------------" << std::endl;
quick_sort(A,0,MAX_ARRAY-1);
for (int i = 0; i < MAX_ARRAY; i++) {
std::cout << A[i] << " ";
}
std::cout << std::endl;
delete [] A;
}

调试分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

输入随机串:

404 411 300 143 462 12 455 288 142 118
^ ^ ^
i j r

开始:
i=-1 j=0 r=9

404 411 300 143 462 12 455 288 142 118
^ ^ ^
i j r

i=-1 j=5 r=9 找出来了,将 i++并且将 i=0 j=5 交换

12 411 300 143 462 404 455 288 142 118
^ ^ ^
i j r
i=0 j=8 r=9
一直到j贴近r了,都没有找出比r小的数据。
将i+1位置和r位置交换。
这样就满足了以i+1位置为中线,左侧数字都比它小,右侧都比它大;
并且返回i+1作为分而治之的两块接着交给后续函数完成递归排序。

12 118 300 143 462 404 455 288 142 411
^ ^ ^
i+1 j r
i=0 j=8 r=9

递归算法将刚刚的返回值i+1当成q,以这个为分隔切开两个数组接着交给
下一轮递归排序来跑。

1. 前面的分隔的数组
s=0,r=q-1

[12] 118 300 143 462 404 455 288 142 411
^
q

2. 后面的分隔的数组
s=q+1,r=r

12 118 [300 143 462 404 455 288 142 411]
^
q

递归下去的算法,又是一个轮回。找到最后一个数字,并且将前面的全部数字
按照左小,右大的方式破开,然后再次来分而治之来处理。

300 143 462 404 455 288 142 411
^ ^ ^
i j r

143 300 462 404 455 288 142 411
^ ^ ^
i j r

143 300 404 462 455 288 142 411
^ ^ ^
i j r


143 300 404 462 455 288 142 411
^ ^ ^
i j r

143 300 404 288 455 462 142 411
^ ^ ^
i j r

143 300 404 288 455 462 142 411
^ ^ ^
i j r

143 300 404 288 142 462 455 411
^ ^ ^
i j r

143 300 404 288 142 462 455 411
^ ^ ^
i+1 j r

最终排序的情况
143 300 404 288 142 411 455 462
^
q

复杂度分析

算法复杂度位于: O(nlgn)~O(nn)。

快排本质上是一个递归二叉树,而且每次树里面都有固定要有n次,所以最凑巧的时候,这棵二叉树是

平衡的,树的高度为 lgn ,最不凑巧的情况就是这棵树一直都偏为一边,这样高度就和 n 相同。

1
2
3
4
5
6
7

1
/ \
2 3
/ \ / \
4 5 6 7