1. 程式人生 > 其它 >排序演算法:選擇排序

排序演算法:選擇排序

選擇排序的基本思想:每一趟從待排序的記錄中選出關鍵字最小的記錄,按順序放到已排序的記錄序列的最後,直到全部排完為止。

1. 簡單選擇排序

簡單選擇排序也稱作直接選擇排序。
演算法過程如圖:

演算法描述:

void SelectSort(SqList &L) {
  for (int i=1; i<L.length; i++) {
    int k = i;
    for (int j=i+1; j<=L.length; j++) {  // 選擇後面序列中最小的值
      if (L[j] < L[k]) k = j;
    }
    if (k != i) {  // 交換L[i]和L[k]
      int temp = L[i];
      L[i] = L[k];
      L[k] = temp;
    }
  }
}

演算法分析:
時間複雜度:\(O(n^2)\)
空間複雜度:\(O(1)\)

演算法特點:

  1. 選擇排序本身是種穩定的排序方法,但圖中表現出不穩定的現象,這是由於所採用的“交換記錄”的策略所造成的的,改變策略,可以寫出不產生“不穩定現象”的選擇排序演算法。
  2. 可用於鏈式結構
  3. 移動記錄次數較少,當每一記錄佔用空間較多時,此方法比直接插入排序快。

2. 樹形選擇排序

樹形選擇排序,又稱錦標賽排序。
演算法過程如圖所示:

這種排序方法尚有輔助儲存空間較多、和“最大值”進行多餘的比較等缺點。
為了改進這個缺點,威洛姆斯在1964年提出了另一種形式的選擇排序————堆排序。

3. 堆排序

堆排序是一種樹形選擇排序。
堆實質上是滿足如下性質的完全二叉樹:樹中所有非終端結點的值均不大於(或不小於)其左、右孩子結點的值。

實現堆排序需要解決如下兩個問題:
(1)建初堆:如何將一個無序序列建成一個堆?
(2)調整堆:去掉堆頂元素,在堆頂元素改變之後,如何調整剩餘元素成為一個新的堆?

1. 調整堆
調整堆過程如圖:

上述過程就像過篩子一樣,把較小的關鍵字逐層篩下去,較大的關鍵字逐層選上來。因此,稱此方法為“篩選法”。

篩選法調整堆演算法描述:

void HeapAdjust(SqList &L, int s, int m) {
  int c = L[s];
  for (int j=2*s; j<=m; j*=2) {
    if (j<m && L[j].key < L[j+1].key) j++;  // j為key較大的記錄的下標
    if (r.key >= L[j].key) break;
    L[s] = L[j];
    s = j;
  }
  L[s] = c;
}

2. 建初堆
只有一個結點的樹必是堆;而在完全二叉樹中,所有序號大於\(\lfloor\)\(\frac{n}{2}\)\(\rfloor\)的節點都是葉子,因此只需利用篩選法,從最後一個非葉子結點\(\lfloor\)\(\frac{n}{2}\)\(\rfloor\)開始,依次將\(\lfloor\)\(\frac{n}{2}\)\(\rfloor\)\(\lfloor\)\(\frac{n}{2}\)\(\rfloor-1\)...、1為序號的節點作為根,分別調整其為堆即可。
建初堆演算法描述:

void CreatHeap(SqList &L) {  // 把無序序列L[1, ..., n]建成大根堆
  int n = L.length;
  for(int i=n/2; i>0; i--) {
    HeapAdjust(L, i, n);
  }
}

3. 堆排序演算法
堆排序的過程:

演算法描述:

void HeapSort(SqList &L) {
  CreatHeap(L);
  for (int i=L.length; i>1; i--) {
    x = L[1];
    L[1] = L[i];
    L[i] = L[1];
    HeapAdjust(L, 1, i-1);
  }
}

演算法分析:
時間複雜度:\(O(nlog_2n)\)
空間複雜度:\(O(1)\)

演算法特點:
(1)不穩定排序
(2)只能用於順序結構,不能用於鏈式結構
(3)初始建堆需要的比較次數較多,因此 記錄數較少時不宜採用。堆排序在最壞情況下時間複雜度為\(O(nlog_2n)\),相對於快速排序最壞情況下\(O(n_2)\)而言是個優點, 當記錄較多時較為高效

步履不停