排序

本文将详细介绍插入排序、希尔排序、堆排序、归并排序、快速排序及快速选择算法,并用Java实现。
另外简单提及外部排序。
1.时间复杂度分析
1)插入排序:O(N^2);——适用于对少量数据接近排序的数据进行排序。
2)希尔排序:
使用希尔增量时(gap=N/2;gap/=2),最坏情形运行时间:O(N^2);
使用Hibbard增量时,最坏情形运行时间:O(N^(3/2));
Sedgewick提出的几种增量序列,最坏情形运行时间是O(N^(4/3));平均运行时间猜测为O(N^(7/6)).
3)堆排序:
给出了至今最佳运行时间:O(NlogN).(主要问题是,使用了一个额外的数组)
4)归并排序:
所使用的比较次数几乎是做少的,最坏情形运行时间:O(NlogN).
它是标准Java类库中泛型排序所使用的算法(泛型排序时,比较是昂贵的)
5)快速排序:——适用于对大量数据进行排序
最坏情形运行时间(枢纽元始终是最小元素):O(N^2);
平均情形运行时间(最好情况下,枢纽元位于中间):O(NlogN);
在C++或对Java基本类型排序中特别有用。
6)快速选择
最坏情形运行时间:O(N^2);
平均情形运行时间:O(N);
7)桶式排序

2.外部排序
以上介绍的排序算法都需要将数据装入内存(内存可直接寻址)。
有一些应用程序,数据量太大装不进内存,此时就需要进行外部排序。
磁带上的元素只能被顺序访问。若只有一个磁带驱动器,那么任何排序算法都将需要O(N^2)次磁带访问。
外部排序对设备的依赖性较强。
基本的外部排序算法使用归并排序中的合并算法。
1)多路合并,k-路 需要2k盘磁带。
需要进行的趟数这里写图片描述,其中N为总的记录数,M为内存可一次容纳的记录数。(每组排过序的记录叫做一个顺串)
2)多项合并,不要求2k盘磁带。
2-路合并:如果顺串的个数是一个**斐波那契数**F(N),那么分配顺串最好的方式是把它们分裂成两个斐波那契数F(N-1)和F(N-2).否则,须用一些哑顺串来填补磁带,将顺串个数补足成一个斐波那契数。
k-路合并:需要k阶斐波那契数用于分配顺串。
3)替换选择(产生顺串的一个算法)
替换选择产生平均长度为2M的顺串。
3.插入排序、希尔排序、堆排序、归并排序、快速排序及快速选择算法的Java实现

    /**
     * 插入排序
     * 时间复杂度:O(N^2),反序输入可以达到该界
     * 若输入数据已预先排序,则运行时间O(N).(内层for循环总是立即判定不成立而终止)
     * 插入排序的运行时间为O(I+N),I为原始数组中逆序数。
     * @param a 待排序数组
     */
    public <Type extends Comparable<? super Type>> void insertionSort(Type[] a){
        //避免明显的使用交换,算法所需时间较短
        int j;
        for(int p=1;p<a.length;p++){
            Type tmp = a[p];
            for(j=p;j>0 && tmp.compareTo(a[j-1])<0;j--){//避免明显的使用交换
                a[j] = a[j-1];
            }
            a[j] = tmp;
        }
    }
    public <Type extends Comparable<? super Type>> void insertionSort1(Type[] a){
            //明显的使用了交换,算法所需时间较长
        for(int i=1;i<a.length;i++){
            for(int j=i;j>0;j--){
                if(a[j].compareTo(a[j-1])<0){
                    Type tmp = a[j];a[j] = a[j-1];a[j-1] = tmp;
                }else{
                    break;
                }
            }
        }
    }
    /**
     * 希尔排序(希尔增量):最坏运行时间:O(N^2)
     * (Hibbard增量的希尔排序最坏运行时间:O(N^(3/2))).
     * @param a
     */
    public <Type extends Comparable<? super Type>> void shellSort(Type[] a){
        int j;
        for(int gap=a.length/2;gap>0;gap=gap/2){
            for(int i=gap;i<a.length;i++){
                Type tmp = a[i];
                for(j=i;j>=gap && tmp.compareTo(a[j-gap])<0;j-=gap){
                    a[j] = a[j-gap];
                }
                a[j] = tmp;
            }
        }
    }
    /**
     * 堆排序(MAX堆)
     * 有至今见到的最佳运行时间:O(NlogN).
     * @param a
     */
    public static <Type extends Comparable<? super Type>> void heapSort(Type[] a){
        for(int i=a.length/2;i>=0;i--){  //创建MAX堆
            percDown(a,i,a.length);
        }
        for(int i=a.length-1;i>0;i--){  //删除堆中最大值,同时完成排序
            swapReferences(a,0,i);
            percDown(a,0,i);
        }
    }
    private static int leftChild(int i){
        return 2*i+1;
    }
    /**
     * 创建MAX堆
     * @param a 待排序数组
     * @param i 下滤起始位置
     * @param n 二叉堆大小
     */
    private static <Type extends Comparable<? super Type>> void percDown(Type[] a,      int i,int n){
        int child;
        Type tmp;
        for(tmp=a[i];leftChild(i)<n;i=child){
            child = leftChild(i);
            if(child!=n-1 && a[child].compareTo(a[child+1])<0){
                child++;
            }
            if(a[child].compareTo(tmp)>0){
                a[i] = a[child];
            }else{
                break;
            }
        }
        a[i] = tmp;
    }
    /**
     * 交换值
     */
     private static <Type extends Comparable<? super Type>> void swapReferences(Type[] a, int i,int n){
        Type tmp = a[i];
        a[i] = a[n];
        a[n] = tmp;
    }
    /**
     * 归并排序:运行时间O(NlogN).
     * 明显问题:合并两个已排序的表用到附加内存(可以审慎的避免)
     * 归并排序使用所有流行排序算法中最少的比较次数
     * !!它是标准Java类库中泛型排序所使用的算法.!!
     * @param a
     */
    public static <Type extends Comparable<? super Type>> void mergeSort(Type[] a){
        Type[] tmpArray = (Type[]) new Comparable[a.length];
        mergeSort(a,tmpArray,0,a.length-1);
    }
    private static <Type extends Comparable<? super Type>> void mergeSort(Type[] a,Type[] tmpArray,int left,int right){
        if(left<right){
            int center = (left+right)/2;
            mergeSort(a,tmpArray,left,center);
            mergeSort(a,tmpArray,center+1,right);
            merge(a,tmpArray,left,center+1,right);
        }
    }
    private static <Type extends Comparable<? super Type>> void merge(Type[] a,Type[] tmpArray,int leftPos,int rightPos,int rightEnd){
        int leftEnd = rightPos-1;
        int tmpPos = leftPos;
        int num = rightEnd-leftPos+1;
        while(leftPos<=leftEnd && rightPos<=rightEnd){
            if(a[leftPos].compareTo(a[rightPos])<0)
                tmpArray[tmpPos++] = a[leftPos++];
            else
                tmpArray[tmpPos++] = a[rightPos++];
        }
        while(leftPos<=leftEnd){
            tmpArray[tmpPos++] = a[leftPos++];
        }
        while(rightPos<=rightEnd){
            tmpArray[tmpPos++] = a[rightPos++];
        }
        for(int i=0;i<num;i++){
            a[rightEnd] = tmpArray[rightEnd--];//???为什么一定要用rightEnd
        }
    }
    /**
     * 快速排序
     * 平均运行时间:O(NlogN).(最好情况下,枢纽元正好位于中间)
     * 最坏运行时间:O(N^2).(枢纽元始终是最小元素)
     * 对于很小的数组,快速排序不如插入排序.
     * @param a
     */
    public <Type extends Comparable<? super Type>> void quickSort(Type[] a){
        quickSort(a,0,a.length-1);
    }
    private static final int CUTOFF = 3;
    private static <Type extends Comparable<? super Type>> void quickSort(Type[] a,int left,int right){
        if(left+CUTOFF<=right){
            Type pivot = median3(a,left,right);
            int i = left; int j = right-1;
            for(;;){
                while(a[++i].compareTo(pivot)<0){   
                }
                while(a[--j].compareTo(pivot)>0){   
                }
                if(i<j)
                    swapReferences(a,i,j);
                else
                    break;
            }
            swapReferences(a,i,right-1);//重置枢纽元

            quickSort(a,left,i-1);//对所有小元素进行排序
            quickSort(a,i+1,right);//对所有大元素进行排序
        }else{
            insertionSort(a,left,right);//对子矩阵进行插入排序
        }
    }
    /**
     * 为快速排序算法选取枢纽元
     * @param a
     * @param left
     * @param right
     * @return
     */
    private static <Type extends Comparable<? super Type>> Type median3(Type[] a,int left,int right){
        int center = (left+right)/2;
        if(a[left].compareTo(a[right])>0)
            swapReferences(a,left,right);
        if(a[center].compareTo(a[left])<0)
            swapReferences(a,left,center);//参考堆排序中此方法
        if(a[center].compareTo(a[right])>0)
            swapReferences(a,center,right);
        swapReferences(a,center,right-1);//枢纽元放在(right-1)位置
        return a[right-1];
    }
    /**
     * 应用于快速排序算法(子矩阵进行插入排序)
     * @param a
     * @param left
     * @param right
     */
    private static <Type extends Comparable<? super Type>> void insertionSort(Type[] a,int left,int right){
        for(int p=left+1;p<right+1;p++){
            Type tmp = a[p];
            int j;
            for(j=p;j>left && tmp.compareTo(a[j-1])<0;j--){
                a[j] = a[j-1];
            }
            a[j] = tmp;
        }
    }
    /**
     * 快速选择
     * 最坏运行时间:O(N^2)
     * 平均运行时间:O(N).
     * @param a 可比较元素的数组
     * @param k
     */
    public <Type extends Comparable<? super Type>> void quickSelect(Type[] a,int k){
        quickSelect(a,0,a.length-1,k);
    }
    private static <Type extends Comparable<? super Type>> void quickSelect(Type[] a,int left,int right,int k){
        if(left+CUTOFF<=right){
            Type pivot = median3(a,left,right);
            int i = left;int j = right-1;
            for(;;){
                while(a[++i].compareTo(pivot)<0){
                }
                while(a[--j].compareTo(pivot)>0){
                }
                if(i<j)
                    swapReferences(a,i,j);//参考堆排序中此方法
                else
                    break;
            }
            swapReferences(a,i,right-1);
            if(k<=i){
                quickSelect(a,left,i-1,k);
            }else if(k>i+1){
                quickSelect(a,i+1,right,k);
            }
        }else{
            insertionSort(a,left,right);//参考快速排序中此方法
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值