希爾排序又稱“縮小增量排序”(Diminishing Incerment Sort),是插入排序的一種,因D.L.Shell於1959年提出而得名。直接插入排序當待排序的記錄個數較少且待排序序列的關鍵字基本有序時,效率較高。希爾排序基於以上兩點,從“減少記錄個數”和“序列基本有序”兩個方面對直接插入排序進行了改進。
[算法思想]希爾排序實質上是採用分組插入的方法。先將整個待排記錄序列分割成幾組,從而減少參與直接插入排序的數據量,對每組分別進行插入排序,然後增加每組的數據量,重新分組,這樣當經過幾次分組排序後,整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。
希爾對記錄的分組,不是簡單地“逐段分割”,而是將相隔某個“增量”的記錄分成一組。1)第一趟取增量d1(d1<n)把全部記錄分成d1個組,所有間隔爲d1的記錄分在同一組,在各個組中進行直接插入排序。
2)第二趟取增量d2(d2<d1),重複上述的分組和排序。
3)依次類推,直到所有的增量dt=1(dt<d(t-1)<...<d2<d1),所有記錄在用一組中進行直接插入排序爲止。
例 已知待排序記錄的關鍵字序列爲{49,38,65,97,76,13,27,49,55,04},請給出用希爾排序排序法進行排序的過程。
1)第一趟取增量d1=5,所有間隔爲5的記錄分在同一組,全部記錄分成5組,在各個組中分別進行直接插入排序,排序結果如上圖第2行所示。
2)第一趟取增量d2=3,所有間隔爲3的記錄分在同一組,全部記錄分成3組,在各個組中分別進行直接插入排序,排序結果如上圖第3行所示。
3)第三趟取增量d3=1,對整個序列進行一趟直接插入排序,排序完成。
希爾排序的算法實現如下所示,預設好的增量序列保存在數組dt[0..t-1]中,整個希爾排序算法需執行t趟。從上述排序過程可見,直接排序算法可以看成一趟增量是1的希爾排序,所以可以改寫直接排序算法,得到一趟希爾排序ShellInsert。在ShellInsert中,具體改寫主要有兩處:
1)前後記錄位置的增量是dk,而不是1;2)r[0]只是暫存單元,不是哨兵。當j<=0,插入位置已找到。
[算法描述]void ShellInsert(int r[],int dk,int Length)
{
/* 對順序表r做一趟增量是dk的希爾排序 */
int i,j;
for(i = dk+1;i < Length; ++i)
{
if(r[i]<r[i-dk]) /* 需將r[i]插入有序增量子表 */
{
r[0]=r[i]; /* 暫時存在r[0] */
for(j = i-dk;j > 0 && r[0] < r[j]; j -= dk)
r[j+dk]=r[j]; /* 記錄後移,直到找到插入位置 */
r[j+dk]=r[0]; /* 將r[0]即原r[i],插入到正確位置 */
}
}
}
void Shellsort(int r[],int dt[],int t,int Length)
{
/* 按增量序列dt[0..t-1]對順序表L作t趟希爾排序 */
for(int k=0;k<t;++k) /* 一趟增量爲dt[t]的希爾插入排序 */
ShellInsert(r,dt[k],Length);
}
[算法分析]1.時間複雜度
當增量大於1時,關鍵字較小的記錄就不是一步步地挪動,而是跳躍式地移動,從而使得在進行最後一趟增量爲1的插入排序中,序列已基本有序,只要作記錄的少量比較和移動即可完成排序,因此希爾排序的時間複雜度較直接插入排序地。當要具體分析,則是一個複雜的問題。
2.空間複雜度
從空間來看,希爾排序和前面兩種排序方法一樣,也只需要一個輔助空間r[0],空間複雜度爲O(1)
[算法特點]1)記錄跳躍式地移動導致排序方法是不穩定的。
2)只能用於順序結構,不能用於鏈式結構。
3)增量序列可以有各種取法,當應該使增量序列中的值沒有除1之外的公因子,並且最後一個增量必須等於1。
4)記錄總的比較次數和移動次數都比直接插入排序要少,n越大時,效果越明顯。所以適合以初始記錄無序、n較大時的情況。
[完整代碼]#include<iostream>
using namespace std;
void ShellInsert(int r[],int dk,int Length)
{
/* 對順序表r做一趟增量是dk的希爾排序 */
int i,j;
for(i = dk+1;i < Length; ++i)
{
if(r[i]<r[i-dk]) /* 需將r[i]插入有序增量子表 */
{
r[0]=r[i]; /* 暫時存在r[0] */
for(j = i-dk;j > 0 && r[0] < r[j]; j -= dk)
r[j+dk]=r[j]; /* 記錄後移,直到找到插入位置 */
r[j+dk]=r[0]; /* 將r[0]即原r[i],插入到正確位置 */
}
}
}
void Shellsort(int r[],int dt[],int t,int Length)
{
/* 按增量序列dt[0..t-1]對順序表L作t趟希爾排序 */
for(int k=0;k<t;++k) /* 一趟增量爲dt[t]的希爾插入排序 */
ShellInsert(r,dt[k],Length);
}
int main()
{
int dt[3]={5,3,1}; /* 增量沒有除1之外的公因子 */
int a[11]={0,49,38,65,97,76,13,27,49,55,8}; /* a[0]是監視哨 */
Shellsort(a,dt,3,11);
for(int i=1;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
[運行結果]