高级排序算法

初级排序方法有直接插入排序、直接交换排序(冒泡)、直接选择排序。对应的高级排序方法分别是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);

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

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

posted @ 2016-04-17 17:44  YoZane  阅读(350)  评论(0编辑  收藏  举报