1. 理论基础
挖坑填数+分治法可以很形象的讲述快速排序:
假设有如下数组 array
:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
72 | 6 | 57 | 88 | 60 | 42 | 83 | 73 | 48 | 85 |
现在我们要对它做升序排序
第一步,取基准数
理论上数组的任一元素都可以作为基准数,这里我们选择第 0 号元素 72,base = array[left]
现在数组可以看成:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
6 | 57 | 88 | 60 | 42 | 83 | 73 | 48 | 85 | |
↑ left | ↑ right |
实际上0号位72仍然存在,但我们通过 base 将 0 号位的 72 存储下来的,此时 0 号位上的元素可以被覆盖,因此逻辑上我们抽象地将 0 号位看成空白,下面的论述仍然是如此
第二步:填坑
第一个坑已经出来,接下来我们就需要填这个坑:从右到左(←)找一个小于base的数
我们找到了
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
6 | 57 | 88 | 60 | 42 | 83 | 73 | 48 | 85 | |
↑ left | ↑ right |
填进去:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
48 | 6 | 57 | 88 | 60 | 42 | 83 | 73 | 85 | |
↑ left | ↑ right |
接下来从左向右(→)找到比base大的数,填充进去
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
48 | 6 | 57 | 88 | 60 | 42 | 83 | 73 | 85 | |
↑ left | ↑ right |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
48 | 6 | 57 | 60 | 42 | 83 | 73 | 88 | 85 | |
↑ left | ↑ right |
依次类推,一个轮回之后,就会变成如下数组
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
48 | 6 | 57 | 42 | 60 | 72 | 83 | 73 | 88 | 85 |
以 base=72
为基准,左边的数都小于它,右边的数都大于它,
接下来再对左右两边进行如此的快排,就能得到一个有序的数组
2. 代码实现
public static void quickSort(int[] array, int left, int right){
if(left < right) {
int l = left, r = right;
int base = array[l];
while (l < r) {
// l小于r, 并且当前元素大于base,则继续向前寻找
while (l < r && array[r] >= base) {
r--;
}
if (l < r) {
array[l] = array[r];
l++;
}
// l小于r, 并且当前元素小于base,则继续向前寻找
while (l < r && array[l] <= base) {
l++;
}
if (l < r) {
array[r] = array[l];
r--;
}
}
array[l] = base;
quickSort(array, left, l - 1);
quickSort(array, r + 1, right);
}
}
3. 时间复杂度
最优情况:
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n),每次都平分
最差情况:
O
(
n
2
)
O(n^2)
O(n2),每次都取到最大或最小,也就变成了冒泡排序
平均情况:
O
(
n
l
o
g
2
n
)
O(nlog_2n)
O(nlog2n)