您的位置:首页 > 其它

简单排序算法实现——快速排序

2013-01-31 15:46 288 查看
快速排序 (Quick Sort) 是使用“分治”思想的又一个排序算法,通过将序列不断分为子序列,最终达到单元素情况来解决问题。

其大致实现过程是从待排序序列中挑选出一个主元 (pivot element,又叫枢纽元),然后将剩下的元素分割成两个部分,一个部分是小元素部分,其中所有的元素都比主元小;另一个是大元素部分,其中所有的元素都比主元大。在这一步分为小元素部分与大元素部分的过程中,每个部分暂不要求有序。然后对两个子序列再递归使用快速排序,从而最终解决问题。

在代码实现中,主要分为以下几个部分:1.暴露在外的public驱动方法QuickSort。2.实现递归的private方法QuickSOrt。3.在递归部分的一开始被调用的Partition分割方法。

首先给出驱动方法代码如下:

//This public method is used as the driven method to actually invoke
//the private QuickSort method
public static <AnyType extends Comparable<? super AnyType>>
void QuickSort(AnyType[] a){
//Invoke private QuickSort method, ranging from start to end.
QuickSort(a,0,a.length-1);
}


在该驱动方法中,调用递归方法,参数中指定了需要递归处理的部分是0~a.length-1,即整个数组部分。

接下来给出处理递归的方法QuickSort:

private static <AnyType extends Comparable<? super AnyType>>
void QuickSort(AnyType[] a, int left, int right){
//This is the cut-off condition which if unsatisfied indicates
//the partition has already gone to a singe-element stage and can thus stopping.
if(left<right){
int pivotPos;
//Partition method will separate the array,
//and also return an Integer indicating the position of pivot.
//This position of pivot will be used for further QuickSort.
pivotPos=Partition(a,left,right);
QuickSort(a,left,pivotPos-1);
QuickSort(a,pivotPos+1,right);
}
}


在该段代码中截止判断条件是 left<right,当该条件不再满足时,有left==right,表明分割已经到了单元素阶段,递归停止并退回。

pivotPos指示在Partition方法完成后的主元的位置,该位置用于标明下一步的递归的范围是 left~pivotPos-1, pivotPos+1~right。

以下给出Partition方法:

private static <AnyType extends Comparable<? super AnyType>>
int Partition(AnyType[] a,int left,int right){
//Use Median-of-three partitioning method.
//After three times swaping, a[left],a[center],[aright] are placed orderly
//So a[center] must be the mean value, it's swaped to the end of the array.
int center=(left+right)/2;
AnyType temp;
if(a[left].compareTo(a[right])>0){temp=a[left];a[left]=a[right];a[right]=temp;}
if(a[left].compareTo(a[center])>0){temp=a[left];a[left]=a[right];a[right]=temp;}
if(a[center].compareTo(a[right])>0){temp=a[center];a[center]=a[right];a[right]=temp;}
temp=a[center];
a[center]=a[right];
a[right]=temp;//Now, a[right] is the pivot element.

int pos=left-1;//Indicate the last element in small part.

for(int j=left;j<right;j++){
if(a[j].compareTo(a[right])<0){
pos++;
temp=a[pos];
a[pos]=a[j];
a[j]=temp;
}
}

//We swap the pivot element back between the small part and large part.
//That position is pos+1.
temp=a[pos+1];
a[pos+1]=a[right];
a[right]=temp;

//We need to return the position of the pivot element.
return pos+1;
}


简述一下对于pivot的选取。一种简单的实现是不作特殊选择,默认使用最后一个元素作为主元。这样易于编程,但是却不太适合,尤其当数据已经是预排序过的,或者逆排序的

时候,这种选取将造成分割的不平衡,而分割的平衡性是快速排序算法的性能保证。在分割最不平衡的最坏情况下,即一个子序列只有一个元素,另一个有n-1个元素,如果该情况持续出现的话,算法时间是O(n^2),效率很低。


另一种选取的方法是随机选取,该种选取方法在实践中可以较好的保持平衡性,但是随机数的生成本身开销较大,在此也不采用。

这里采用的是三数中值分割法(Median-of-Three Partitioning),通过left,center,right三个数的中值来作为主元。经过三次交换,三个值将有序排列,此时center的元素必然是中值,即为主元,将它交换到数列的末尾。

代码中的pos指向小元素区的最后一个元素,通过这个下标的分割,既可分开小元素区与大元素区。

循环下标 j 将遍历除主元外的每一个元素。如果该元素比主元大,不作操作,只将 j 加一推向下一个位置。如果该元素比主元小,则将 i 的值先加一推进,然后将 i 现在指向的实际是比主元大的值与 j 指向的当前操作值交换,继而保证了分割的正确性。

在 j 遍历完成以后,分割也就完成了,然后将主元交换到中间,即与大元素区的第一个元素,即pos+1的位置的元素交换。最后partition方法返回主元的位置pos+1。

最后附上用于测试的main方法,整个程序可以正确运行:

public static void main(String[] args){
System.out.println("Quick Sort:");

Integer[] elements={3,4,1,8,10,2,0,6,5};

System.out.print("Original elements: ");
for(int i=0;i<elements.length;i++) System.out.print(elements[i]+" ");
System.out.println();

QuickSort(elements);

System.out.print("After sorting: ");
for(int i=0;i<elements.length;i++) System.out.print(elements[i]+" ");
System.out.println();
}


探讨一下快速排序与归并排序的联系与区别:
1.两者都是采用了分治的递归思想,平均算法复杂度相同。

2.归并排序是先分割,在分割的时候只须简单的一份为二,分的时候不要求有序,在整个的分割完成后合并的时候通过精心的合并过程来达到有序,因此是先分后治,治中求序的过程;快速排序也是先分割,但是并不是简单的分割,而是通过选择分割的方法,在分割的时候就保证有序性,相反在合并的时候已经没有实质性的操作,只是简单的退出递归即可,因此是在分的时候就消除了逆序。

3.归并排序保证每次都是一分为二,因此其性能是有保证的。快速排序的分割并不要求平均,实际上分割是依赖于主元的选取的,因此主元的选取是会影响整体性能的。

4.在以上给出的算法中,实现的是快速排序的原地排序版本(in-place),故无需额外空间,但是牺牲了稳定性。相对的,归并排序需要O(n)的额外空间,空间性能较差。

关于时间复杂度:

平均时间复杂度是O(nlogn)

最优时间复杂度是O(nlogn)

最差时间复杂度是O(n^2)

附图一张,以便理解记忆:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: