詳談內部排序之希爾排序

希爾排序


基本思想:

​ 先將整個待排記錄序列分割成若干子序列,分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。

技巧:

​ 子序列的構成不是簡單地“逐段分割”,而是將相隔某個增量dk的記錄組成一個子序列,讓增量dk逐趟縮短(例如依次取5,3,1),直到dk=1爲止。

優點:

​ 讓關鍵字值小的元素能很快前移,且序列若基本有序時,再用直接插入排序處理,時間效率會高很多。


希爾排序的過程:

在這裏插入圖片描述
如上圖所示,我們根據增量來取兩個值來進行比較大小,循環一遍之後,就將增量減小,直到增量爲1後停止減小。

例題:

例:關鍵字序列 T=(49, 38, 65, 97, 76, 13, 27, 49*, 55, 04) 請寫出希爾排序的具體實現過程。
在這裏插入圖片描述
上面的表格就是每次循環完成的結果 ,下面我們來詳細的解釋每一個步驟。
在這裏插入圖片描述

希爾排序主要算法描述:

void ShellInsert ( SqList &L, int dk )
{ //一趟希爾插入排序
  //1.前後記錄位置的增量是dk;
  //2.L.r[0]只是暫存單元,不是哨兵。當j<=0時,插入位置已找到
 for (i=dk+1; i<=L.length; ++i )
      if ( L.r[i].key< L.r[i-dk].key) {//將R[i]插入有序增量子表
      L.r[0] = L.r[i]; // 暫存在R[0]
      for (j=i-dk; j>0 && (L.r[0].key< L.r[j].key);j -= dk)   
          L.r[j+dk] = L.r[j]; // 記錄後移,查找插入位置
      L.r[j+dk] = L.r[0]; // 插入 
  }
}//ShellInsert

void ShellSort (SqList &L, int dlta[ ], int t)
{
    // 按增量序列dlta[0..t-1]對順序表L作希爾排序
    for (k=0; k<t; ++k)
        ShellInsert( L, dlta[k]);  // 一趟增量爲dlta[k]的插入排序
} // ShellSort

希爾排序算法分析:

​ 開始時dk 的值較大,子序列中的對象較少,排序速度較快;隨着排序進展,dk 值逐漸變小,子序列中對象個數逐漸變多,由於前面工作的基礎,大多數對象已基本有序,所以排序速度仍然很快。

​ 希爾排序的時間複雜度較直接插入排序低。希爾排序的分析是一個複雜的問題,因爲它的時間是和所取“增量”序列的函數密切相關。

​ 增量序列可有各種取法,但需注意應使增量序列中的值沒有除1之外的公因子,並且最後一個增量值必須等於1。其分析是一個複雜的過程,因爲它的時間是取“增量”序列的函數,這涉及一些數學問題尚未解決,因此,到目前爲止,還沒有求得一個組好得增量序列,但有大量的局部結論。

​ 對特定的待排序對象序列,可以準確地估算關鍵碼的比較次數和對象移動次數。但想要弄清關鍵碼比較次數和對象移動次數與增量選擇之間的依賴關係,並給出完整的數學分析,還沒有人能夠做到。

​ Knuth利用大量的實驗統計資料得出,當n很大時,關鍵碼平均比較次數和對象平均移動次數大約在 n1.25 到 1.6n1.25 的範圍內。這是在利用直接插入排序作爲子序列排序方法的情況下得到的。

希爾排序的性能分析:

時間效率:O(n(log2n)^2) ——所選增量比較合理

空間效率: O(1) —— 因爲僅佔用1個緩衝單元

算法的穩定性:不穩定

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章