Java:排序算法整理

JAVA排序算法

按平均时间将排序分为四类:

(1)平方阶(O(n^2))排序
     一般称为简单排序,例如直接插入、直接选择和冒泡排序;
(2)线性对数阶(O(nlgn))排序
     如快速、堆和归并排序;
(3)O(n1+£)阶排序
     £是介于0和1之间的常数,即0<£<1,如希尔排序;
(4)线性阶(O(n))排序
     如桶、箱和基数排序。

各种排序方法比较

 简单排序中直接插入最好,快速排序最快,当文件为正序时,直接插入和冒泡均最佳。

影响排序效果的因素

选择合适的排序方法应综合考虑下列因素:
  ① 待排序的记录数目n;
  ② 记录的大小(规模);
  ③ 关键字的结构及其初始状态;
  ④ 对稳定性的要求;
  ⑤ 语言工具的条件;
  ⑥ 存储结构;
  ⑦ 时间和辅助空间复杂度等。

不同条件下,排序方法的选择

(1) 若n较小(如n≤50),可采用直接插入或直接选择排序。
    当记录规模较小时,直接插入排序较好,它会比选择更少的比较次数;
    但当记录规模较大时,因为直接选择移动的记录数少于直接插人,所以宜用选直接选择排序。
    这两种都是稳定排序算法。
(2) 若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜(这里的随机是指基准取值的随机,原因见上的快速排序分析);
    这里快速排序算法将不稳定。
(3) 若n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
    快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
    堆排序虽不会出现快速排序可能出现的最坏情况。但它需要建堆的过程。这两种排序都是不稳定的。
    归并排序是稳定的排序算法,但它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。
(4) 特殊的箱排序、基数排序
    它们都是一种稳定的排序算法,但有一定的局限性:
      1、关键字可分解。
      2、记录的关键字位数较少,如果密集更好
      3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。

排序算法的稳定性

(1) 稳定的:如果存在多个具有相同排序码的记录,经过排序后,这些记录的相对次序仍然保持不变,则这种排序算法称为稳定的。
    插入排序、冒泡排序、归并排序、分配排序(桶式、基数)都是稳定的排序算法。
(2) 不稳定的:否则称为不稳定的。
    直接选择排序、堆排序、shell排序、快速排序都是不稳定的排序算法。

Java代码

直接插入排序

简单的插入排序,每次比较后最多移掉一个逆序,因此与冒泡排序的效率相同。但它在速度上还是要高点,这是因为在冒泡排序下是进行值交换,而在插入排序下是值移动,所以直接插入排序将要优于冒泡排序。直接插入法也是一种对数据的有序性非常敏感的一种算法。在有序情况下只需要经过n-1次比较,在最坏情况下,将需要n(n-1)/2次比较。

public void insertSort(int[] data){
    int i,j,temp;
    for(i=1;i0 && temp

冒泡排序

在最优情况下只需要经过n- 1次比较即可得出结果,(这个最优情况那就是序列己是正序,从100K的正序结果可以看出结果正是如此),但在最坏情况下,即倒序(或一个较小值在最 后),下沉算法将需要n(n-1)/2次比较。所以一般情况下,特别是在逆序时,它很不理想。它是对数据有序性非常敏感的排序算法。

public void bulbleSort(int[] data){
    for(int i=data.length-1;i>0;i--){
        for(int j=0;j

交换排序

每次取出数组的第一个数与后面的数据进行比较,如果大于,则交换位置,从而保证每次交换都能找到该次循环的最小值,它的比较次数一定:n(n- 1)/2,时间复杂度为O(n^2)。对数据的有序性不敏感。

public void changeSort(int[] data){
    for(int i=0;idata[j]){
                int temp = data[j];
                data[j] = data[i];
                data[i] = temp;
            }
        }
    }
}

快速排序

通过一次交换能消除多个逆序,这样可以减少逆序时所消耗的扫描和数据交换次数。在最优情况下,它的排序时间复杂度为O(nlog2n)。 即每次划分序列时,能均匀分成两个子串。但最差情况下它的时间复杂度将是O(n^2)。即每次划分子串时,一串为空,另一串为m-1(程序中的100K正 序和逆序就正是这样,如果程序中采用每次取序列中部数据作为划分点,那将在正序和逆时达到最优)。从100K中正序的结果上看“快速排序”会比“冒泡排 序”更慢,这主要是“冒泡排序”中采用了提前结束排序的方法。有的书上这解释“快速排序”,在理论上讲,如果每次能均匀划分序列,它将是最快的排序算法, 因此称它作快速排序。虽然很难均匀划分序列,但就平均性能而言,它仍是基于关键字比较的内部排序算法中速度最快者。

public void quickSort(int[] data,int left,int right){
    int i,j,temp;
    i = left;
    j = right;
    if(left > right)
        return;
    temp = data[left];
    while(i != j){
        while(temp<=data[j] && j>i)
            j--;
        if(j>i)
            a[i++] = data[j];
        while(temp>=data[i] && j>i)
            i++;
        if(j>i)
            a[j--] = data[i];
    }
    data[i] = temp;
    quickSort(data,left,i-1);
    quickSort(data,i+1,right);
}

选择排序

简单的选择排序,它的比较次数一定:n(n- 1)/2。也因此无论在序列何种情况下,它都不会有优秀的表现(从上100K的正序和反序数据可以发现它耗时相差不多,相差的只是数据移动时间),可见对数据的有序性不敏感。它虽然比较次数多,但它的数据交换量却很少。所以我们将发现它在一般情况下将快于冒泡排序。

public void selectSort(int[] data){
    int i,j,k;
    for(int i=0;i

希尔排序

增量的选择将影响希尔排序的效率。但是无论怎样选择增量,最后一定要使增量为1,进行一次直接插入排序。但它相对于直接插入排序,由于在子表中每进行一次比较,就可能移去整个经性表中的多个逆序,从而改善了整个排序性能。希尔排序算是一种基于插入排序的算法,所以对数据有序敏感。

public void shellSort(int[] data){
    int len = data.length;
    int d = len/2;
    while(d != 0){
        for(int i=d;i=0){
                if(data[j]>data[j+d]){
                    int temp = data[j+d];
                    data[j+d] = data[j];
                    data[j]= temp;
                }
                j--;
            }
        }
        d=d/2;
    }
}

文章来源:
1) 资料1
2) 资料2
3) 资料3

11/20/2014 3:32:56 PM


你可能感兴趣的:(Java)