zoukankan      html  css  js  c++  java
  • 高级排序算法

    初级排序方法有直接插入排序、直接交换排序(冒泡)、直接选择排序。对应的高级排序方法分别是shell排序、快速排序、堆排序

    另外高级排序算法还有归并排序。

    下面演示对数组R[]进行非降排序:

    直接插入排序:

        public static int[] InsertSort(int[] R){
            int n =R.length;
            for(int j =1;j<n;j++){
                int i= j-1;
                int K = R[j];
                while(i>=0&&K < R[i]){
                    R[i+1]=R[i];
                    i--;
                }
                R[i+1] = K;
            }
            return R;
        }

    如果将数组的第一个元素置为一个最小值,那么算法中的 while(i>=0&&K<R[i]){……} 可以改写成 while(K<R[i]){……} ;这样就可以节省比较 i 的时间。

    直接插入排序(冒泡排序):

    每一趟排序能够找出一个最大值,并把它交换到最终位置。因此每一趟的扫描次数可以减 1 ;

        public static int[] BoubleSort(int[] R){
            int n = R.length;
            for(int j =n-1;j>=1;j--){
                for(int i =1;i<=j;i++){
                    if(R[i]<R[i-1]){
                        int t =R[i];
                        R[i] = R[i-1];
                        R[i-1] = t;
                    }
                }
            }
            return R;
        }

    冒泡排序还可以通过添加标记的方法减少不必要的比较次数,如下代码用 Bound 控制每趟扫描的边界,这样就可以加速算法。

        public static int[] BSort(int[] R){
            int n = R.length;
            int j = n;
            int Bound = n;
            while(Bound!=0){
                Bound = j;
                j = 0;
                for(int i =1;i<Bound;i++){
                    if(R[i]<R[i-1]){
                        int t =R[i];
                        R[i] = R[i-1];
                        R[i-1] = t;
                        j = i;
                    }
                }
            }
            return R;
        }    

    直接选择排序:

    直接选择排序每次从待排序列中选出一个最小值(用一个数m来记录最小值的下标,提高算法效率)放在它的最终位置,每次选最小元素的时间复杂度是 O(n) ,有 n 个元素,n*O(n) -> 算法的效率是 O(n*n).

        public static int[] SelectSort(int[] R){
            int n =R.length;
            for(int j =0;j<n;j++){
                int K = R[j];
                int m = j;
                int i = j+1;
                while(i<n){
                    if(R[i]<K){
                        K = R[i];
                        m =i;
                    }
                    i++;
                }
                int t = R[j];
                R[j] = R[m];
                R[m] = t;
            }
            return R;
        }

    高级排序算法:

    shell 排序:

    shell 排序是目前对小规模数据(少于 50 个元素)进行排序的最有效的算法(优于快速排序),它的主要思想是通过逐减(由大到小)增量对数据进行分组,对每组数据单独使用直接插入排序算法进行组内排序;当增量减小到 1 时,所有的数据变成一组,经过最后一趟直接插入排序后得到有序数列。 shell 排序之所以效率很高,主要是因为当增量很大时,组内一次交换减少的乱序对数量相当可观,因而 shell 排序是很有效的排序算法。

    复杂度: shell 排序的时间复杂度与其所选取的增量关系密切,目前已知最好的序列是{1,4,10,23,57,132,301,701,1750……},当渐减增量序列形如 2^p*3^q 时shell排序时间复杂度是 O(n*(log(n))^2) ;

    下面我以 n/(2^i),i =1,2,3……为渐减序列进行 shell 排序:

    public class ShellSort{
        static int[] a = {0,2,1,8,3,46,85,14,85,78,56,4,5,69,8755,1,12,54,15,5,};
        public static void main(String[] args){
            int n = a.length;
            show(a);
            shell(a);
            System.out.println("shell排序:");
            show(a);
        }
        public static void shell(int[] R){
            //int[] s = {1,4,10,23,57,132,301,701,1750};//目前已知的最好的增量序列
            int n = R.length;
            int h = n;
            while(h != 1){
                for(int i =0;i<h;i++){
                    h = h/2;
                    InsertSort(R,i,n-1,h);
                }
            }
        }
        public static void InsertSort(int[] R,int a,int b,int h){
            for(int j = a+h;j<=b;j+=h){
                int i =j-h;
                int K = R[j];
                while(i>=a&&K<R[i]){
                    R[i+h]=R[i];
                    i-=h;
                }
                R[i+h] = K;
            }
        }
        public static void show(int[] a ){
            for(int i =0;i<a.length;i++){
                System.out.print(a[i]+" ");
            }
            System.out.println();
        }    
    }
    View Code

    快速排序:

    快速排序是一种效率特别高的排序算法,主要思想是首先确定一个基准 R[m] (通常选用第一个元素,当待排序列基本有序时,总是选第一个数作为基准会使算法效率特别低,这时就需要使用三者取中法把R[m],R[n],R[(m+n)/2]中的中间元素交换到R[0]处作为基准);

    (1)算法开始时首先初始化两个指针i,j,一个指向基准元素(加 1 操作后指向基准元素的下一个元素),一个指向最末元素下一个(减 1 操作后指向最末元素);

    (2)然后第一个指针 ( i ) 往后移寻找大于基准的元素找到后停止移动,第二个指针(j)从后往前寻找小于基准的元素找到后停止移动;

    (3)当 i,j 未发生交叉时,R[i]<--> R[j];

    (4)当 i,j 有交叉时 基准<-->R[j],本趟排序结束,基准交换到它的最终位置(可以根据这一性质设计复杂度为 O(n) 的查找低 i 大元素的算法);

    (5)分别对 j  前后的序列进行下一趟快速排序。

    class QSort{//排序结果为非降
        static int[] a = {0,2,1,8,3,46,85,14,85,78,56,4,5,69,8755,1,12,54,15,5,};
        public static void main(String[] args){
            //快速排序
            int n = a.length;
            show(a);
            quickSort(a,0,n-1);        
            System.out.println("快速排序");
            show(a);
        }
        public static void quickSort(int[] R,int a,int b){//R[a]~R[b] 
            if(b-a<1){
                return;
            }
            if(b-a==1){
                if(R[a]>R[b]){
                    int t = R[a];
                    R[a] = R[b];
                    R[b] = t;
                }
                return;
            }
            int n = b;
            int m = a;
            int mid = (m+n)/2;
            int t;
            if(R[n]<R[mid]){
                t = R[n];
                R[n] = R[mid];
                R[mid] = t;
            }
            if(R[n]<R[m]){
                t = R[n];
                R[n] = R[m];
                R[m] = t;
            }
            if(R[m]<R[mid]){
                t = R[m];
                R[m] = R[mid];
                R[mid] =t;
            }//三者取中,中值存在R[m]中
            int i =m;
            int j =n+1;
            while(i<j){
                i++;
                while(R[i]<R[m]){
                    i++;
                }
                j--;
                while(R[j]>R[m]){
                    j--;
                }
                if(i<j){
                    t = R[i];
                    R[i] = R[j];
                    R[j] = t;
                }
            }
            t = R[j];
            R[j] = R[m];
            R[m] = t;
            quickSort(R,m,j-1);
            quickSort(R,j+1,n);
            return;
        }
        public static void show(int[] a ){
            for(int i =0;i<a.length;i++){
                System.out.print(a[i]+" ");
            }
            System.out.println();
        }
    }
    View Code

     

    堆排序:

    堆排序包含两个过程,第一个过程是初始建堆(由下向上);第二个过程是堆排序(自上而下),把堆顶与逐末元素交换,再重建堆。

    上面两个过程都需要使用算法 Restore(R,f,e) 来调整堆中的元素,每次调整时父节点与儿子结点比较,如果儿子结点比他大就把比较大的儿子结点与父节点互换位置,然后重复此操作直至堆底。

    public class HeapSort{
        static int[] a = {0,2,1,8,3,46,85,14,85,78,56,4,5,69,8755,1,12,54,15,5,};
        public static void main(String[] args){
            HeapSort(a);
            System.out.println("堆排序:");
            show(a);
        }
        public static void HeapSort(int[] R){
            int n = a.length;
            for(int i =n-1;i>=0;i-- ){//重建堆:从下向上
                Restore(a,i,n-1);
            }
            for(int i =n-1;i>0;i--){//堆排序:从上向下
                Restore(a,0,i);
                int t = a[0];
                a[0] = a[i];
                a[i] = t;
            }        
        }
        public static void Restore(int[] R,int f,int e){
            int j =f;
            int m;
            while(2*j+1<e){
                if(2*j+2<e&&R[2*j+1]<R[2*j+2]){
                    m = 2*j+2;
                }else{
                    m = 2*j+1;
                }
                if(R[j]<R[m]){
                    int t = R[j];
                    R[j] = R[m];
                    R[m] = t;
                    j = m;
                }else{
                    j = e;//跳出循环
                }
            }
        }
        public static void show(int[] a ){
            for(int i =0;i<a.length;i++){
                System.out.print(a[i]+" ");
            }
            System.out.println();
        }    
    }
    View Code

     归并排序:

    归并排序与快速排序一样都利用了分治思想,归并排序把排序的问题转化为了合并两个数组的问题,简单易懂。算法的复杂度主要由归并的趟数和数据的规模决定。归并的趟数近视等于 log2(n) (把归并的过程看成一个树结构,树的高度近似是 log2(n) ),每一趟关键词比较次数复杂度为 O(n) ;整个过程的时间复杂度是 n*log(n) 。

    public class MSort{
        static int[] a = {0,2,1,8,3,46,85,14,85,78,56,4,5,69,8755,1,12,54,15,5,};
        public static void main(String[] args){
            show(a);
            a = MSort(a);
            System.out.println("MSort:");
            show(a);
        }
        public static int[] MSort(int[] R){
            int n =R.length;
            int[] X = new int[n];
            int length = 1;
            while(length<n){
                int i =0;
                while(i+2*length-1<n){
                    Merge(R,i,i+length-1,i+2*length-1,X);
                    i = i+2*length;
                }
                if(i+length-1<n-1){
                    Merge(R,i,i+length-1,n-1,X);
                }
                length = 2*length;
                int[] t = X;
                X = R;
                R = t;
            }
            return R;
        }
        public static void Merge(int[] R,int a,int b,int c,int[] X){
            int i = a;
            int j = b+1;
            int k = a;
            while(k<=c){
                while(i<=b&&j<=c){
                    if(R[j]<R[i]){
                        X[k] = R[j];
                        j++;
                    }else{
                        X[k] = R[i];
                        i++;
                    }
                    k++;
                }
                while(i<=b){
                    X[k] = R[i];
                    i++;
                    k++;
                }
                while(j<=c){
                    X[k] = R[j];
                    j++;
                    k++;
                }
            }
        }
        public static void show(int[] a ){
            for(int i =0;i<a.length;i++){
                System.out.print(a[i]+" ");
            }
            System.out.println();
        }    
    }
    View Code

     

     总结:

    时间复杂度 : 所有的简单排序算法再加上直接插入排序的改进算法(shell排序)都是平方阶简单排序算法:O(n^2),shell排序:O(n*(logn)^2);

    高级排序算法中的快速排序,合并排序,堆排序的时间复杂度都是线性对数阶 O(n*logn);

    具体的时空复杂度差异:快速排序 < 归并排序 < 堆排序 . 快速排序当值无愧是性能冠军。 

    稳定性:高级算法中除归并算法外都不稳定;初级排序算法中除直接选择排序外都是稳定的排序算法。

  • 相关阅读:
    扑克牌顺子
    反转字符串
    左旋转字符串
    和为S的两个数
    C++中substr()详解
    STL库中的equal_range()
    和为S的连续正序列
    数组中只出现一次的数
    二叉树的深度
    mysql找安装路经,更改密码
  • 原文地址:https://www.cnblogs.com/yuanzhenliu/p/5401242.html
Copyright © 2011-2022 走看看