定义
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
简单点~
算法描述
快速排序使用分治法来把一个序列(list)分为两个子序列(sub-lists)。具体算法描述如下:
我们可以把快速排序看着三个步骤:
1.选择轴点:在待排序列中,按照某种方式挑出一个元素,作为轴点。
2.分区操作:以该基准值在序列中的实际位置,把序列分成两个子序列,一边是比它大的值,另外一边是比它小的值(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3.递归(recursive):对两个子序列进行递归的快排,直到序列为空或者只有一个元素。
轴点:左/右侧的元素,均不比它更大/小
以轴点为界,原序列的划分自然实现:[low, high)=[low, mid)+[mid] + (mid, high)
在原始序列中轴点未必存在,但是通过适当交换,可使任一元素转换为轴点。
在有序序列中所有元素皆为轴点,反之亦然。
快速排序:就是将所有元素逐个转换为轴点的过程
主算法: 快速找到轴点,借助二分式的递归,就可以快速写出快速排序算法。
public static void quickSort(int[] A, int low, int high) {
if (high - low < 2) {//单元素区间自然有序,比如[3,4)
return;
}
//先构造轴点mid
int mid = partition(A, low, high - 1);//注意patition函数使用两端封闭区间
quickSort(A, low, mid);//前缀排序
quickSort(A, mid + 1, high);//后缀排序
}
通过构造轴点的方法有两种,即partition()方法的实现有两种方式。
方法1:
- 首先选择轴点候选作为培养对象,取做首元素。整个序列分为候选轴点、L(前缀,L<= pivot)、U(大小未知)、G(后缀,pivot<=G)四部分。
初始U为原始序列,L、G为空。
low和high交替向内侧移动,使他们彼此靠近。low每向后移动一次,U中的元素加入到L中,L向右扩展一单元。high每向前移动一次,U中的元素加入到G中,G向左扩展一单元。low与high指向同一个位置时,将选定的轴点候选者放入这个位置。候选者就成了名副其实的轴点。
下面图理解构造轴点的过程。
public static int partition (int[] A, int low, int high) {
int pivot = A[low] ;
while (low < high) {
//从U的尾元素开始迭代
while (low < high && pivot <= A[high]) {
high--;
}
if (low < high) {
A[low] = A[high];
}
while (low < high && A[low] <= pivot) {
low++;
}
if (low < high) {
A[high] = A[low];
}
}
//候选轴点归位(从而名副其实)
A[low] = pivot;
return low;
}
方法2:
轴点归位变种:
首先选择轴点候选作为培养对象,取做首元素。整个序列分为候选轴点、L(前缀,L<= pivot)、G(后缀,pivot<=G)、U(大小未知)四部分。L边界[low,mid],G[mid+1,k-1],U[k,hi]决定。
初始U为原始序列,L、G为空。
low和high交替向内侧移动,使他们彼此靠近。low每向后移动一次,U中的元素加入到L中,L向右扩展一单元。high每向前移动一次,U中的元素加入到G中,G向左扩展一单元。low与high指向同一个位置时,将选定的轴点候选者放入这个位置。候选者就成了名副其实的轴点。
//从U的首元素开始迭代,根据x元素的大小,将其归位到L或者G子序列中。
最后一步 候选轴点归位(从而名副其实):根据L小G大的特性,候选轴点安插在L和G的交接处,G不变,L的末元素被替换为候选轴点,也就是L的末元素和候选轴点交换
public static int partition(int[] A, int low, int high) {
//此方法轴点元素可随机的选取
int pivot = A[low];//选取第一个元素作为轴点
int mid = low;
//从U的首元素开始迭代
for (int k = low + 1; k <= high; k++) {//k指向U,自左向右考察每个[k]
if (A[k] < pivot) {//若[k]小于轴点,将其归入L,做法:则将其与[mid+1](也就是G的第一个元素)交换,L向右扩展到mid+1
++mid;
int temp = A[k];
A[k] = A[mid];
A[mid] = temp;
}//隐藏的else,在这种情况下简明的令k递增一个单位。因为if也需要k递增,所以统一放在for循环的更新环节
}
//候选轴点归位(从而名副其实),根据L小G大的特性,候选轴点安插在L和G的交接处,G不变,L的末元素被替换为候选轴点,也就是L的末元素和候选轴点交换
A[low] = A[mid];
A[mid] = pivot;
return mid;//返回轴点的位置
}
测试方法:返回 0,1,1,2,3,3,4,7,8,9,12,22,65
public static void main(String args[]) {
int[] A = {7, 1, 2, 0, 9, 3, 12, 1, 8, 3, 4, 65, 22};
quickSort(A, 0, A.length);
for (int i : A) {
System.out.print(i + ",");
}
}
算法分析
最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)