【算法学习日志】排序算法-归并排序和快速排序

归并排序

1、原地归并的抽象方法

public static void merge(Comparable[] a, int lo, int mid, int hi){
    int i = lo, j = mid + 1; //分别指向两个子数组的头部

    for(int k = lo; k < hi; k++){
        aux[k] = a[k]; //将数组a赋值到辅助数组aux
    }

    for(int k = lo; k < hi; k++){
        if       (i > mid)                a[k] = aux[j++]; //此时左半边用尽
        else if  (j > hi)                 a[k] = aux[i++]; //此时右半边用尽
        else if  (less(aux[j], aux[i]))   a[k] = aux[j++]; //选小的
        else                              a[k] = aux[i++];
    }
}

2、基本思想:

        先将数组分成两半分别排序,然后将结果归并起来

3、算法分析

        时间复杂度:O(NlogN)

        空间复杂度:O(N)

4、自顶向下的归并排序

public class Merge{
    private static Comparable[] aux;

    public static void sort(Comparable[] a){
        aux = new Compareble[a.length]; //外部分配辅助数组,避免每次归并都创建新数组
        sort(a, 0, a.length - 1);
    }

    private static void sort(Cpmparable[] a, int lo, int hi){
        if(hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, lo, mid);        // 左半边排序
        sort(a, mid+1, hi);      // 右半边排序
        merge(a, lo, mid, hi);   // 归并结果
    }
}

基本思想:如果能将两个子数组排序,就能通过归并两个子数组来将整个数组排序

【举例】长度为16的数组归并排序的调用轨迹

sort(a, 0, 15)
    sort(a, 0, 7)
        sort(a, 0, 3)
            sort(a, 0, 1)
                merge(a, 0, 0, 1)
            sort(a, 2, 3)
                merge(a, 2, 2, 3)
            merge(a, 0, 1, 3)
        sort(a, 4, 7)
            sort(a, 4, 5)
                merge(a, 4, 4, 5)
            sort(a, 6, 7)
                merge(a, 6, 6, 7)
            merge(a, 4, 5, 7)
        merge(a, 0, 3, 7)
    sort(a, 8, 15)
        sort(a, 8, 11)
            sort(a, 8, 9)
                merge(a, 8, 8, 9)
            sort(a, 10, 11)
                merge(a, 10, 10, 11)
            merge(a, 8, 9, 11)
        sort(a, 12, 15)
            sort(a, 12, 13)
                merge(a, 12, 12, 13)
            sort(a, 14, 15)
                merge(a, 14, 14, 15)
            merge(a, 12, 13, 15)
        merge(a, 8, 11, 15)
merge(a, 0, 7, 15)

说明:使用插入排序处理小规模的子数组,一般可以将归并排序的运行时间缩短

5、自底向上的归并排序

public class MergeBu{
    private static Comparable[] aux;

    public static void sort(Comparable[] a){
        int N = a.length;
        aux = new Comparable[N];
        for(int sz = 1; sz < N; sz = sz+sz){             //sz子数组大小
            for(int lo = 0; lo < N-sz; lo += sz+sz){     //lo子数组头索引
                merge(0, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
            }
        }
    }
}

基本思想:先归并微型数组,在成对地归并得到子数组

说明:适用于使用链表组织的数据

快速排序

public class Quick{

    public static void sort(Comparable[] a){
        StdRandom.Shuffle(a); // 随机打乱初始值
        sort(a, 0, a.length - 1);
    }

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

1、基本思想:

        当两个子数组都有序时,整个数组自然有序

2、切分

private static int partition(Comparable[] a, int lo, int 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; // 当i和j相遇时主循环退出
        exch(a, i, j);
    }
    exch(a, lo, j); // 将v = a[lo]放入正确位置
    return j;
}

        (1)满足条件

                ①对于某个j, a[j]已经排定

                ②a[lo] 到 a[j-1]中的所有元素都不大于a[j]

                ③a[j+1]到a[hi]中的所有元素都不小于a[j]

        (2)策略

                先随意地取a[lo]作为切分元素,即将会被排定的元素,然后从数组的左端开始向右扫描直到找到一个大于等于它的元素,再从数组的右端开始向左扫描直到找到一个小于等于它的元素。交换这两个没有排定元素的位置。如此循环,当两个指针相遇时,将切分元素a[lo]和左子数组的最右元素a[j]交换然后返回j即可。

        (3)算法改进——三向切分的快速排序

public class Quick3way{
    private static void sort(Comarable[] 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++;
        }
        sort(a, lo, lt - 1);
        sort(a, gt + 1, hi);
    }
}

          策略:

        ①a[i] < v,将a[lt]和a[i]交换,将lt和i加一;

        ②a[i] > v,将a[gt]和a[i]交换,将gt减一;

        ③a[i] == v,将i加一

        

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值