快速排序(Quick Sort)
在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
算法描述
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
例子
数组: 72 6 57 88 60 42 83 73 48 85
pivot = 72
i - 72
j - 85 向左读取
从j开始向左读取遍历,找到小于等于pivot替换i
第一次: 48 6 57 88 60 42 83 73 48 85
i - 48
j - 48
从i开始向右读取遍历,找到大于等于pivot替换j
第二次: 48 6 57 88 60 42 83 73 88 85
重复以上两次
第三次: 48 6 57 42 60 42 83 73 88 85
第四次: 48 6 57 42 60 72 83 73 88 85
按照pivot = 72分为两个分区按照上面的步骤进行
最终 6 42 48 57 60 72 73 83 85 88
结束
算法复杂度
空间复杂度:
时间复杂度:
排序不稳定,原来相等的两个参数排在前面的可能排在后面。
代码实现
public static void quick_sort(int[] s, int l, int r) {
System.out.println(Arrays.toString(s));
if (l < r){
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j){
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
运行结果
输入数组:int[] arr = {72,6,57,88,60,42,83,73,48,85};
[72, 6, 57, 88, 60, 42, 83, 73, 48, 85]
[48, 6, 57, 42, 60, 72, 83, 73, 88, 85]
[42, 6, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 83, 73, 88, 85]
[6, 42, 48, 57, 60, 72, 73, 83, 88, 85]
[6, 42, 48, 57, 60, 72, 73, 83, 88, 85]
[6, 42, 48, 57, 60, 72, 73, 83, 85, 88]
[6, 42, 48, 57, 60, 72, 73, 83, 85, 88]
但是按照上面的步骤,这时候我们可以看到和演算结果一致。
快速排序还有很多改进版本,如随机选择基准数,区间内数据较少时直接用另的方法排序以减小递归深度。有时间我这再进行统计学习。