快速排序

快速排序

前情回顾

前面介绍了几种简单的排序方法

快速排序特点

1.实现简单
2.适用于各种不同的输入数据
3.比其他排序算法快得多
4.原地排序(只需要一个很小的辅助栈)
5.所需时间和NlgN成正比。

算法思想

将一个数组分成两个子数组,左子数组和右子数组。当两个子数组分别有序后,整个数组就完成排序。

public static void sort(Comparable[] a){
        StdRandom.shuffle(a);      //将原数组随机打乱,消除算法对输入的依赖
        sort(a,0,a.length-1);
    }

    private static void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo)  return;
        int j=partition(a,lo,hi);  //切分
        sort(a,lo,j-1);        //将左半部分a[lo..j-1]排序
        sort(a,j+1,hi);        //将右半部分a[j+1..hi]排序
    }

快速排序递归地将子数组a[lo…hi]排序,先用partition()方法将a[j]放到一个合适的位置(也就是这个元素最终的位置)然后再递归调用将其他位置的元素排序。,
那如何将一个数组分成两个子数组呢?依据哪个元素进行划分,也就是上面代码中的partition方法。这个过程使得数组满足下面三个条件:

  • 对某个j,a[j]已经排定
  • a[lo]到a[j-1]中的所有元素都不大于a[j];
  • a[j+1]到a[hi]中的所有元素都不小于a[j]。
    **快速排序其实就是通过递归调用这个partition切分方法来排序的。

一般策略是

  • 随意选择a[lo]作为切分元素,即那个将会被排定的元素,
  • 然后从数组的左端开始向右扫描直到找到一个大于等于它的元素,
  • 再从数组的右端开始向左扫描直到找到一个小于等于它的元素。
  • 这两个元素是显然没有排定的,因此交换它们的位置。
  • 如此继续,我们就可以保证左指针i的左侧元素都不大于切分元素,右指针j的右侧元素都不小于切分元素。
  • 当两个指针相遇时,我们只需要将切元素a[lo]和左子数组最右侧的元素a[j]交换,然后返回j即可。
private static int partition(Comparable[] a,int lo,int hi){
        //将数组切分为a[lo..i-1],a[i],a[i+1..hi]
        int i=lo,j=hi+1;      //左右扫描指针
        Comparable v=a[lo];   //切分元素
        while(true){
            //扫描左右,检查扫描是否结束并交换元素
            while(less(a[++i],v))  if(i==hi)  break;
            while(less(v,a[--j]))  if(j==lo)  break;
            if(i>=j) break;
            exch(a,i,j);
        }
        exch(a,lo,j);        //将v=a[j]放入正确的位置
        return j;            //a[lo..j-1]<=a[j]<=a[j+1..hi]达成
    }

改进的快速排序

如果待排序数组中有大量相同的元素,则可将数组分为三部分
1.左子数组,元素均小于比较元素v
2.中间子数组,元素均等于比较元素v
3.右子数组,元素均大于比较元素v

public static void sort(Comparable[] a){
        StdRandom.shuffle(a);      //将原数组随机打乱,消除算法对输入的依赖
        sort(a,0,a.length-1);
    }

    private static void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo)  return;
        int lt=lo,i=lo+1,gt=hi;
        Comparable v=a[lo];
        while(i<=gt){
            int cmp=a[i].compareTo(v);
            if(cmp<0)         exch(a,lt++,i++);
            else if(cmp >0)   exch(a,i,gt--);
            else              i++;
        }//现在是a[lo..lt-1]<v=a[lt..gt]<a[gl+1..hi]成立
        sort(a,lo,lt-1);
        sort(a,gt+1,hi);
    }

这段代码的切分能够将和切分元素相等的元素归位,这样它们就不会被包含在递归调用处理的子数组中了。对于存在大量重复元素的数组,这种方法比标准的快速效率高得多。

完整代码(java)

/**
 * @author zhangjinglong
 * @date 2019-06-20-23:04
 * 快速排序
 */

public class Quick {
    public static void sort(Comparable[] a){
        StdRandom.shuffle(a);      //将原数组随机打乱,消除算法对输入的依赖
        sort(a,0,a.length-1);
    }

    private static void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo)  return;
        int j=partition(a,lo,hi);  //切分
        sort(a,lo,j-1);        //将左半部分a[lo..j-1]排序
        sort(a,j+1,hi);        //将右半部分a[j+1..hi]排序
    }



    private static int partition(Comparable[] a,int lo,int hi){
        //将数组切分为a[lo..i-1],a[i],a[i+1..hi]
        int i=lo,j=hi+1;      //左右扫描指针
        Comparable v=a[lo];   //切分元素
        while(true){
            //扫描左右,检查扫描是否结束并交换元素
            while(less(a[++i],v))  if(i==hi)  break;
            while(less(v,a[--j]))  if(j==lo)  break;
            if(i>=j) break;
            exch(a,i,j);
        }
        exch(a,lo,j);        //将v=a[j]放入正确的位置
        return j;            //a[lo..j-1]<=a[j]<=a[j+1..hi]达成
    }






    private static boolean less(Comparable v,Comparable w){
        return v.compareTo(w)<0;//升序
    }
    private static void exch(Comparable[] a,int i,int j){
        //交换数组中的两个元素
        Comparable t=a[i];
        a[i]=a[j];
        a[j]=t;
    }

    private static void show(Comparable[] a){
        //在单行中打印数组
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+"  ");
        }
        System.out.println();
    }
    public static boolean isSorted(Comparable[] a){
        //测试数组元素是否有序
        for (int i = 1; i < a.length; i++) {
            if(less(a[i],a[i-1])) return false;//如果数组中有相邻的任意两个元素逆序,则返回false
        }
        return true;
    }

    public static void main(String[] args) {
        String[] s={"I","A","B","Z","C","F"};
        sort(s);
        assert isSorted(s);//用于检验我们的排序算法是否正常运行
        show(s);

    }


}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值