您的位置:首页 > 其它

关于快速排序

2017-03-05 20:31 169 查看
快速排序是对冒泡排序的一种改进。关于冒泡排序请参考上篇。快速排序的排序效率高,实用性强;它是原址排序,但不稳定;就平均性能而言,它是最好的排序方法。快速排序是基于分治思想的一种算法,先对原数组进行划分,而后对划分后的两个数组用同样的排序操作。分治法有分割、解决,处理三步,其中最核心部分为第一步:划分parition。
划分:首先选择一个基准pivot,将数组A[1...n]划分为两个数组A[1...k-1]和A[k+1...n] , 前者中的值都 小于pivot,后者中的值都大于pivot,A[k]=pivot,返回k值(注意:此时pivot即A[k]已经在最终的位置,因此它不在这两个数组中的任何一个之中);
解决:对上步的两个数组分别进行快速排序,递归的进行便得到有序数组。合并:由于它是原址排序,因此无需进行合并。关于划分:(1)假设数组第一个值为pivot,并将其存储在key中。设两个变量i、j。首先j先从后向前遍历,如果碰到值大于等于pivot,什么都不做并将j继续向前移动,否则将A[j]的值与A[i]的值交换,j暂停;i从前向后遍历,若碰到的值小于pivot,则继续向后移动不做任何操作,一直碰到一个元素大于pivot,再将此时的A[i]与A[j]值交换,i暂停。再将j由前值继续向前移动,所做与上述操作类似,直到i与j相遇为止,便得到一个划分。其代码如下int parition(int a[],int low,int high){    int key = a[low]; //int key = a[0];    while(low<high){//结束条件:两者相遇之前都是可以的        while(low<high && a[high]>=key)//key保存的是第一个值,先从最后面的开始            high--;        if(a[high]<key)            swap(&a[low],&a[high]);        while(low<high && a[low]<=key)//再从前面开始            low++;        if(a[low]>key)            swap(&a[low],&a[high]);    }//while    return low;}(2)或者不将A[i]和A[j]的值进行交换操作,而是直接将一个值赋给后面的值,类似挖坑填坑的方式。首先将第一个值放到key,挖一个坑;再将A[j]放到上一个坑中,那么A[j]处又多了一个坑,如此进行下去,只需最后将key值放到A[i]中(最后i与j值是相同的)。代码如下:int parition2(int a[],int low,int high){    int key = a[low];    while(low < high){        while(low<high && a[high]>=key)            high--;        if(a[high] < key)            a[low] = a[high];        while(low<high && a[low]<=key)            low++;        if(a[low] > key)            a[high] = a[low];    }//while    a[low] = key;//挖坑的话,需要处理最后一次    return low;}(3)《算法导论》中有一种与之不同的划分方法,它以最后一个值作为pivot,赋值到key,low与i之间存放的是小于等于pivot的值,i+1与j-1之间存放的是大于pivot的值,j之后的值还未判断。初始化i为low-1处的值,j为low处的值。(初始化书上说是为了满足循环不变量就是low、i、j之间的值)判断A[j]与key的值,若A[j]<key,先把i值加一,再将A[j]与A[]交换,并将j的值加1;若A[j]>key,j加一;当j到达数组最后结束。若按照《算法导论》上代码实现如下:int parition3(int a[],int low,int high){    int i = low - 1;    for(int j=low;j<high;j++){        if(a[j]<a[high])            swap(&a[++i],&a[j]);    }//for    swap(&a[++i],&a[high]);    return i;}此处i的取值会超出数组的范围,所以更喜欢下面这种方式,经试验两种都是可以,只是把i的取值 变了一下:int parition4(int a[],int low,int high){    int i= low;    int key = a[high];    for(int j=low;j<high;j++){        if(a[j] < key)            swap(&a[i++],&a[j]);    }//for    swap(&a[i],&a[high]);    return i;}java实现代码如下:package com.hanlin;
public class QuickSort {    static int partition(int[] A,int p,int q){        int x = A[p];        int i = p;        for(int j=p+1;j<=q;j++){            if(A[j]<=x){                i++;            int temp = A[j];            A[j] = A[i];            A[i] = temp;            }//if        }//for        int temp = A[p];        A[p] = A[i];        A[i] = temp;        return i;}
public static void quicksort(int[] A,int p,int q){    if(p<q){        int r = partition(A,p,q);        quicksort(A,p,r-1);        quicksort(A,r+1,q);    }}public static void main(String[] args) {int[] A = {4,65,34,87,1,3,0,85};int a = partition(A,0,7);System.out.println("a = "+a);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}System.out.println();quicksort(A,0,A.length-1);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}
}
}总结:上述有两种快速排序的算法,每种有一些细节方面的不同,但不碍大局。第一种是《王道》等教科书上经常采用的严老师的一种方法,但是出现多个while感觉非常不爽!而《算法导论》中的这种方法真是完美、简单,只需要一个for循环即可。虽然前者更容易理解,但我更倾向于后者。目前我所写的只是关于算法的实现,算法的时间空间复杂度等部分先不描述。修改的时候,发现快排的优化已经忘了,等以后再补充一下。
参考:
1:王道--《数据结构》2、《算法导论》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: