排序

来源:互联网 发布:补水的水乳推荐知乎 编辑:程序博客网 时间:2024/05/24 06:56

本文将详细介绍插入排序、希尔排序、堆排序、归并排序、快速排序及快速选择算法,并用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 0
原创粉丝点击