• 排序算法(2)—归并排序,快速排序


    一.归并排序

    分治思想
    思想:将两个有序的数组归并成一个有序数组。

    原地归并的抽象方法

    (下面两种归并方法用到):
    创建一个适当大小的数组,然后将两个有序数组一个个有序的放入所创建的数组中(在此造成了额外的空间存储问题)。

    方法签名:merge(array,lo,hi),将子数组a[lo...mid],a[mid+1...hi]归并,并将结果放入大a[lo...hi].
    

    原地归并的抽象方法:

        private static int[] aux ;
    
        public static void mergeSort(int[] a, int lo, int mid, int hi){
            int i = lo,j = mid+1;
            for(int k = 0; k<=hi; k++)
                aux[k] = a[k] ;
            for(int k = lo; k<=hi; k++)
                if(i>mid)   
                    a[k] = aux[j++];
                else if(j>hi)   
                    a[k] = aux[i++];
                else if(a[j]<a[i])
                    a[k] = aux[j++];
                else    
                    a[k] = aux[i++];
        }

    自顶向下的归并排序

    此类排序使用标准的递归方式进行
    

    实现如下:

        //消除输入依赖
        public static void sort(int[] arr)
        {
            aux = new int[arr.length];  创建的额外数组存储新的排序好的数组
            sort(arr, 0, arr.length-1);
        }
    
        private static void sort(int[] a, int lo, int hi)
        {
            if(hi<=lo)  return;
            int mid = lo+(hi-lo)/2;
    
            sort(a,lo,mid);
            sort(a,mid+1,hi);
    
            mergeSort(a,lo,mid,hi);  //调用原始归并的抽象方法
        }

    复杂度
    时间复杂度:NlogN
    空间复杂度:N
    对于长度为N的任意数组,需要1/2NlgN到NlgN次比较。
    最多需要访问数组6NlgN次。

    自底向上的归并排序

    思想:先归并微型数组,然后再归并得到的子数组。(翻倍归并)
    

    实现如下:

            public static void sortBU(int[] arr)
            {
            int N = arr.length;
            aux = new int[arr.length];
            for(int size = 1; size < N; size = size+size)
                for(int lo = 0; lo<N-size; lo += size+size)
                    mergeSort(arr, lo, lo+size-1, 
                            Math.min(lo+size+size-1, N-1));     
            }  //最后一个子数组的大小,只有在数组大小是size的偶数倍的时候才会等于size(否则它会比size小).
    

    复杂度
    时间复杂度:NlogN
    空间复杂度:N
    对于长度为N的任意数组,需要比较次数和访问数组次数与自顶向下相同。

    二.快速排序

    运用最为广泛的排序算法
    同样是分治算法思想
    思想:
    根据切分元素,把数组分为两部分,左边小于(大于)切分元素,右边大于(小于)切分元素。
    实现如下:

        private static void quickSort(int[] a, int lo,int hi)
        {
            if(lo>=hi) return;
            //partition()是切分方法
            int j = partition(a,lo,hi);
            quickSort(a,lo,j-1);
            quickSort(a,j+1,hi);        
        }

    根据切分方法,可以将快速排序分为两类:
    常规的切分 and 三向切分

    1. 常规切分

        private static int partition(int[] a,int lo, int hi)
        {
            int i = lo,j = hi+1;
            int v = a[lo];
            while(true)
            {
                while(a[++i]<v)
                    if(i == hi) break;
                while(v<a[--j]) 
                    if(j == lo) break;
                if(i>=j) break;
                exch(a,i,j);
            }
            exch(a,lo,j);
            return j;
        }

    复杂度:
    时间复杂度:NlgN
    空间复杂度:lgN
    对于长度为N的无重复数组排序,平均需要~2NlnN次比较,一级1/6的交换。
    最多需要N^2次比较,但那时可以用随机打乱数组来防止这种最坏情况。

    2. 三向切分

    常规切分的改进
    对于存在大量相同数据的排序尤为有效快速
    实现:

        private static void quick3waySort(int[] a, int lo, int hi){
            if(hi<=lo)
                return;
            int lt = lo,i = lo+1, gt = hi;
            int v = a[lo];
            while(i<=gt)
            {
                int flag = a[i] - v;
                //下面判断决定由小到大.
                if(flag<0)
                    exch(a,lt++,i++);  //交换a[lt]和a[i]位置.
                else if(flag>0)
                    exch(a,i,gt--);
                else
                    i++;
            }
            quick3waySort(a, lo, lt-1);
            quick3waySort(a, gt+1, hi);
        }

    复杂度:
    时间复杂度:N~NlogN
    空间复杂度:lgN
    对于大小为N的数组,需要~(2ln2)NH次比较。H为由主键值出现频率定义的香农信息量。
    它是对于包含大量重复数据的最优排序算法。

    归并算法和快速算法比较
    快速排序特点:原地排序,只需要一个很小的辅助栈,空间复杂度仅为lgN。
    但是相对于归并排序此类的稳定排序,要更注意避免低劣性能的产生,经常会导致在实际运用中性能只有平方级别。

    参照算法第四版


    内容原创,转载请注明出处
    
  • 相关阅读:
    MySQL常见问题(包括忘记root密码)
    MySQL运维面试基础题目
    系统根目录可用空间低于20%,清理mysql binary logs日志空间
    服务检测是否正常运行的shell脚本
    CentOS下安装 MySQL5.5
    问题处理--ceph集群告警: pgs inconsistent修复方案
    十、Spring中常用注解-分层整理
    七、SXSSFWorkbook生成大excle,避免内存溢出
    一、FreeMarker实现对js和css压缩
    九、web.xml理解
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739772.html
Copyright © 2020-2023  润新知