快速排序
概述:
快速排序(Quick Sort)是一种有效的排序算法。虽然算法在最坏的情况下运行时间为O(n^2),但由于平均运行时间为O(nlogn),并且在内存使用、程序实现复杂性上表现优秀,尤其是对快速排序算法进行随机化的可能,使得快速排序在一般情况下是最实用的排序方法之一。快速排序被认为是当前最优秀的内部排序方法。
给出一个n长度的无序数组,对其进行从小到大排序。例如给出无序数组a:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 6 | 3 | 8 | 7 | 5 | 1 | 9 |
基本步骤:
(1)确立一个比较关键值(一般设数组的第一个值为关键值,这里以数组a的第一个元素的值6为关键值,即k=6)。
(2)设置两个标志位分别指向数组的第一位和最后一位(begin=0;end=6)。
标志 | begin | end | |||||
下标 | 0 | 1 | 2 | 3 | 4 | 6 | 6 |
数值 | 6(k) | 5 | 9 | 7 | 3 | 1 | 8 |
(3)(条件:begin<end)将end指向的数值与k值比较,若a[end]<k,交换a[begin]和a[end],否则end前移一位,重复本步骤直到找到 a[end]<k, 即可以交换(将数值比k值小的元素排到k值前面,注:a[end]=k时不能交换)
找到可交换的位置(a[end]=1 < k=6):
标志 | begin | | end | ||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 6(k) | 5 | 9 | 7 | 3 | 1 | 8 |
交换后:
标志 | begin | end | |||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 1 | 5 | 9 | 7 | 3 | 6(k) | 8 |
(4)(条件:begin<end)将begin指向的数值与k值比较,若a[begin]>k,交换a[begin]和a[end],否则begin后移一位,重复本步骤直到找到a[begin]>k,即可以交换(将数值比k值大的元素排到k值后面,注:a[begin]=k时不能交换)
找到可交换的位置(a[begin]=9 > k=6):
标志 | begin | end | |||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 1 | 5 | 9 | 7 | 3 | 6(k) | 8 |
交换后:
标志 | begin | end | |||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 1 | 5 | 6(k) | 7 | 3 | 9 | 8 |
(5)判断begin<end?若小于则再跳转至(3)步骤,直至begin==end,此时:
标志 | begin、end | ||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 1 | 5 | 3 | 6(k) | 7 | 9 | 8 |
可以看出经过一次排序后所有比k=6小的数都排到了k的后面,而比k大的数都排到了k的前面。但a[0]~a[2]和a[4]~a[6]仍然是无序的。所以还要对k两边的部分(看成两个新的数组)单独进行上述的排序。所以对每一个要进行排序的数组都要先进行一次上述排序再分解出新的两个数组,对分解出的数组同样进行上述排序,重复此步骤,直至不能再分。
public static void sort(int[] a,int low,int high){
//key指向begin位
int begin=low,end=high,key=a[low];
while(begin<end){
//从右向左遍历找到第一个比key小的数,与key所指的元素交换位置
//如果没有比关键值小的,end--,直到遇到比关键值小的次层循环结束,
//执行后面的交换程序
while(begin<end&&a[end]>=key){
end--;
}
//交换(交换过后key指向end位)
if(a[end]<=key){int temp=a[end];a[end]=a[begin];a[begin]=temp;}
//从前往后比较找到第一个比key大的数,与key所指的元素交换位置
//如果没有比关键值大的,star++,直到遇到比关键值大的次层循环结束,
//执行后面的交换程序
while(begin<end&&a[begin]<=key){
begin++;
}
//交换(交换过后key指向begin位)
if(a[begin]>=key){int temp=a[begin];a[begin]=a[end];a[end]=temp;}
}
//此时循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,
//右边的值都比关键值大,但是两边的顺序不一定是有序的,进行下面的递归调用
//对key值两边的数组再次进行排序,直至数组不可再分
if(begin>low) sort(a,low,begin-1);//递归排序左边序列
if(end<high) sort(a,end+1,high); //递归排序右边序列
}
前面提到当a[begin]==k或a[end]==k时不能交换,例如a[end]=k,交换了两个数值(因为两个数是相等的实际上并没有改变数组),而在下一层while循环中begin未做任何自加就有a[begin]=k,又进行了交换,同理下一层循环中end未做任何自减就有a[end]=k,又进行了交换......begin和end的位置没有作出任何改变而数组一直在进行交换数值,程序陷入了死循环中!