1. 程式人生 > >幾種初級排序演算法的總結

幾種初級排序演算法的總結

@TOC

幾種初級排序演算法的總結

排序就是將一組物件按照某種邏輯順序重新排列的過程。

在本文中我們使用的操作:

  • 遍歷陣列
  • 比較兩個物件(本文中所有程式碼示例中的less方法)
  • 交換兩個物件 (本文中所有程式碼示例中的exch方法)

less 和 exch方法的原始碼:

private static boolean less(Comparable v, Comparable w) {
   return (v.compareTo(w) < 0);
}

private static void exch(Comparable[] a, int i, int j) {
   Comparable swap = a[i];
   a[i] = a[j];
   a[j] = swap;
} 

選擇排序

選擇排序的基本思路就是為每一個位置選擇對的物件:

  1. 選擇第一個位置;
  2. 從陣列中未被選擇的物件中選擇最小(或最大)的物件放入當前選擇的位置;
  3. 選擇下一個位置,並重復2直到所有位置都有物件;

程式碼示例:

public static void sort(Comparable[] a) {
    int n = a.length;
    for (int i = 0; i < n; i++) {    //選擇位置的迴圈
        int min = i;
        for (int j = i+1; j < n; j++) {     //選擇剩餘物件中最小元素
            if (less(a[j], a[min])) 
               min = j;
        }
        exch(a, i, min);    //交換位於選中位置上的元素和應該位於該位置的元素
    }
}

插入排序

插入排序的基本思路是將物件移動到已經有序的陣列中適當的位置,類似打撲克牌時將新接的牌插入適當位置:

  1. 選中當前陣列的第二個位置的物件;
  2. 比較當前選擇的物件與位於它前面的物件,根據比較結果,如果兩者現在相對位置是不對的,則互動它們,重複2.直到當前選擇的物件前面沒有元素或者,它和前面元素的相對位置是正確的;(一步一步往前挪)
  3. 選擇下一個位置的元素,並重復2直到陣列末尾;

示例程式碼:

public static void sort(Comparable[] a) {
    int n = a.length;
    for (int i = 1; i < n; i++) {     //選擇需要挪動的物件
        for (int j = i; j > 0; j--) {    //一步一步往前挪
            if(less(a[j], a[j-1]))  //如果當前元素比前一個小,則交換位置
               exch(a, j, j-1);
            else
               break;      //已經挪到了對的位置,去挪下一個
        }
    }
}

希爾排序

希爾排序是一種基於插入排序的快速排序演算法;對於大規模的亂序陣列,插入排序會很慢,因為它只交換相鄰的物件,元素要一點一點的往前挪;希爾排序的基本思路是分別對間隔h的元素組成的陣列進行排序,使得原陣列h有序,然後逐步減小h,如下圖:
Alt text

  1. 選取一個h;
  2. 對間隔h的物件組成的陣列進行插入排序;
  3. 減小h,重複2一直到h為1;

程式碼示例:

public static void sort(Comparable[] a) {
    int n = a.length;
    // 3x+1 increment sequence:  1, 4, 13, 40, 121, 364, 1093, ... 
    int h = 1;
    while (h < n/3) h = 3*h + 1;     //取一個大於等於陣列長度三分之一的h
    while (h >= 1) {       
        // h-sort the array
        for (int i = h; i < n; i++) {    //使用插入排序對間隔h的物件組成的陣列進行排序
            for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
                exch(a, j, j-h);
            }
        }
        h /= 3;   //按3的倍數減小h
    }
}

效能與特性

  • 穩定性:選擇排序的穩定性與具體實現有關,主要是判斷大小的條件對物件相等的時候的處理,在我們的實現中它是不穩定的,因為它會互動兩個相等的物件;插入排序是穩定的,因為挪動到相等元素時會停止;而基於插入排序的希爾排序卻是不穩定的,因為分組的過程中會間接打亂相等物件的相對位置。
  • 空間:這三個演算法都是直接在原陣列上進行操作,並沒有申請額外空間,且無遞迴,所以空間複雜度為O(1)。
  • 時間:選擇排序的時間複雜度為O($ N^2 ) O ( N ) O ( );插入排序介於O(N) 到 O( N^2$) 之間,主要依賴於陣列的有序程度,陣列的有序程度越高,演算法的效率越好;希爾排序大概是O(NlogN) ,同樣和陣列的有序程度相關,不過通過分組使得隨機情況下演算法的效能比插入排序更好。

**一點猜想:**從選擇排序到希爾排序的效能變化,可能對應於演算法對已排序物件的資訊的利用程度的變化;比如一個數組中a、b、c是已經排好相對位置的元素,此時決定元素d的位置時,如果我們知道d>c,那麼可以推理得出d>b以及d>a,選擇排序對此類資訊利用的相對較少,而插入排序相對利用的多些。

參考資料