快速排序

快速排序的核心思想也是分治法,分而治之。它的实现方式是每次从序列中选出一个基准值,其他数依次和基准值做比较,比基准值大的放右边,比基准值小的放左边,然后再对左边和右边的两组数分别选出一个基准值,进行同样的比较移动,重复步骤,直到最后都变成单个元素,整个数组就成了有序的序列。

快速排序的关键之处在于切分,切分的同时要进行比较和移动,这里介绍一种叫做单边扫描的做法。

我们随意抽取一个数作为基准值,同时设定一个标记 mark 代表左边序列最右侧的下标位置,当然初始为 0 ,接下来遍历数组,如果元素大于基准值,无操作,继续遍历,如果元素小于基准值,则把 mark + 1 ,再将 mark 所在位置的元素和遍历到的元素交换位置,mark 这个位置存储的是比基准值小的数据,当遍历结束后,将基准值与 mark 所在元素交换位置即可。

代码实现:

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
public static void sort(int[] arr) {
sort(arr, 0, arr.length - 1);
}

private static void sort(int[] arr, int startIndex, int endIndex) {
if (endIndex <= startIndex) {
return;
}
//切分
int pivotIndex = partitionV2(arr, startIndex, endIndex);
sort(arr, startIndex, pivotIndex-1);
sort(arr, pivotIndex+1, endIndex);
}

private static int partition(int[] arr, int startIndex, int endIndex) {
int pivot = arr[startIndex];//取基准值
int mark = startIndex;//Mark初始化为起始下标

for(int i=startIndex+1; i<=endIndex; i++){
if(arr[i]<pivot){
//小于基准值 则mark+1,并交换位置。
mark ++;
int p = arr[mark];
arr[mark] = arr[i];
arr[i] = p;
}
}
//基准值与mark对应元素调换位置
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}

极端情况

快速排序的时间复杂度和归并排序一样,O(n log n),但这是建立在每次切分都能把数组一刀切两半差不多大的前提下,如果出现极端情况,比如排一个有序的序列,如[ 9,8,7,6,5,4,3,2,1 ],选取基准值 9 ,那么需要切分 n - 1 次才能完成整个快速排序的过程,这种情况下,时间复杂度就退化成了 O(n2),当然极端情况出现的概率也是比较低的。

所以说,快速排序的时间复杂度是 O(nlogn),极端情况下会退化成 O(n2),为了避免极端情况的发生,选取基准值应该做到随机选取,或者是打乱一下数组再选取。

另外,快速排序的空间复杂度为 O(1)。

🍭支持一根棒棒糖!