关于快速排序

快速排序是对冒泡排序的一种改进。关于冒泡排序请参考上篇。
快速排序的排序效率高,实用性强;它是 原址排序,但不稳定;就平均性能而言,它是最好的排序方法。
快速排序是基于分治思想的一种算法,先对原数组进行划分,而后对划分后的两个数组用同样的排序操作。分治法有分割、解决,处理三步,其中最核心部分为第一步: 划分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、《算法导论》













  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值