快速排序,利用了分治法的思想,是一种就地算法。
最坏复杂度为 o(n^2)
,期望情况下为 o(nlogn)
,取决于枢纽元的选择
思路——指针交换法
我们以一次快速排序为例
其中,n
就是我们选取的枢纽元,n
左侧的数小于等于n,右侧的大于n。这样我们再递归排序左右两侧元素即可。
难点:
- 枢纽元的确立,如果我们选取边界的话,可能会遇到以下情况: 此时就会产生最坏复杂度。极其不利
- 左右元素的确立,我们必须要保证左边的元素小于等于n,右侧的大于n。那么该怎么做呢?
划分方法
以一次排序为例:
- 我们选取左侧元素为枢纽元
- 设立两个指针,分别指向最左侧和最右侧元素
- 首先把最右侧指针对应元素和枢纽元对比,若大于,指针右移;小于则停止移动,移动权交由左侧指针
- 左侧指针进行比较,若小于枢纽元,指针右移;若大于,指针停止移动
- 此时,左右指针均已停止移动,交换两指针对应元素。指针继续移动
- 直至左右指针重合
- 交换枢纽元和重合元素
至此,一次排序完成
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
//划分方法
private static int partition(int[] arr, int leftIndex, int rightIndex) {
//枢纽点
int pivot = arr[leftIndex];
int left = leftIndex; //为什么不能是 leftIndex+1 ?
int right = rightIndex;
while(left != right){
//判断右侧部分 ②
while(left<right && pivot < arr[right]){
right--;
}
//判断左侧部分 ①
while (left<right && arr[left] <= pivot){ //短路表达式
left++;
}
if( left > right){
break;
}
//此时,①和②都已经停止循环,这说明它们均遇到了不满足条件的数值,所以我们需要进行交换
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
//枢纽值和重合点交换
int temp = arr[left];
arr[left] = arr[leftIndex];
arr[leftIndex] = temp;
//left即为left和right的重合点
return left;
}
上述代码有两点极其需要注意的地方:
- 最开始的枢纽点、左右端点的赋值。最开始,我是图省事值赋值给pivot,结果一直出错,应该是引用问题。
- 判断左右部分,为什么要先判断右侧部分呢?(尚未解决)
总代码
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
public static void main(String[] args) {
int[] arr = {4,9,5,8,6,9,2,11,6,32,49,21,7};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr)); //输出 [2, 4, 5, 6, 6, 7, 8, 9, 9, 11, 21, 32, 49]
}
private static void quickSort(int[] arr, int left, int right){
//当要排序的数组长度小于等于1时,返回
if(left >= right){
return;
}
//获得枢纽元
int pivot = partition(arr, left, right);
//根据枢纽元对左右两侧排序
quickSort(arr, left, pivot-1); //注意此处索引 -1 ,因为枢纽元位置已确定
quickSort(arr, pivot+1, right);
}
//划分方法
private static int partition(int[] arr, int leftIndex, int rightIndex) {
//枢纽点
int pivot = arr[leftIndex];
int left = leftIndex;
int right = rightIndex;
while(left != right){
while(left<right && pivot < arr[right]){
right--;
}
while (left<right && arr[left] <= pivot){ //短路表达式
left++;
}
if( left > right){
break;
}
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
int temp = arr[left];
arr[left] = arr[leftIndex];
arr[leftIndex] = temp;
return left;
}
思路——挖坑法
挖坑法比较简单,不易出错
详细可参考:漫画:什么是快速排序?