希爾排序(Shell Sort),是插入排序的一種。是直接插入排序的改進版,它是非穩定排序算法。其得名於它的提出者D.L.Shell,Shell Sort等算法的提出打破了“排序算法不可能突破O(n^2)”的魔咒,它將排序算法的時間複雜度提成到了O(nlogn),不可能超越的O(n^2)徹底成爲了歷史。
既然希爾排序是直接插入排序的改進版,那麼它肯定是在直接插入排序的優勢上做文章。瞭解直接插入排序的都知道,當待排序的序列基本有序的時候,我們只需要進行少量的插入操作,就可以完成整個記錄的排序工作,這時的直接插入排序是最高效的。希爾排序就是先將待排序序列調整到基本有序,然後再對整個序列進行一次直接插入排序,這樣的話會比直接進行直接插入排序效率高的多。所謂的基本有序,就是小的關鍵字基本在前面,大的基本在後面,不大不小的在中間。可是怎樣才能將序列調整爲基本有序呢?這裏我們採取跳躍分割的策略,即將相聚某個“增量”的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序後得到的結果是基本有序而不是局部有序。當然,一次調整並不能保證基本有序,我們可以逐漸縮小這個“增量”,每縮小一次進行一次調整,直到“增量”縮小爲1,我們就認爲它基本有序了,然後再進行一次“增量”爲1的調整,也就是對整個序列進行一次直接插入排序,就可以得到我們想要的有序序列。
那麼“增量”到底該以怎樣的形式進行縮小呢?這目前還是一個數學難題,這裏我們將以increment=length;increment=increment/2;increment>=1的形式縮小,increment即是“增量”,length是序列長度。也就是說,初始增量是length/2,其後每調整一次縮小一半,直到爲1。代碼如下:
void ShellSort(int a[],int length)
{
int i,j;
int temp;
int increment=length;
do{
increment=increment/2;
for(i=increment;i<length;i++)
{
if(a[i]<a[i-increment])
{
temp=a[i];
for(j=i-increment;j>=0 && a[j]>temp;j-=increment)
{
a[j+increment]=a[j];
}
a[j+increment]=temp;
}
}
}while(increment>1);
}
假設我們所要排序的序列爲a[9]={9,1,5,8,3,7,4,6,2},那麼length=9,do-while進入第一次循環,“增量”increment=4,排序操作如下:
第一步:比較a[0]與a[0+4],9>3,將3賦給”哨兵”temp,進入代碼第13行循環,將9後移increment位,也就是4位,執行j-=increment,不符合循環條件,循環結束,將”哨兵”temp的值賦給a[j+increment],也就是a[0]的位置,這一步操作就是一個直接插入排序的操作。
第二步:比較a[1]與a[1+4],1<7,不滿足if條件,循環進入下一層
第三步:比較a[2]與a[2+4],5>4,進入if語句(過程與第一步相同不再闡述)。
第四步:比較a[3]與a[3+4],8>6,進入if語句(過程與第一步相同不再闡述)。
第五步(這一步比較特殊):比較a[4]與a[4+4],9>2,將2賦給”哨兵”temp,進入代碼第13行循環,將9後移increment位,也就是4位,執行j-=increment後,仍然符合for循環條件,循環進入下一層,將3後移increment位,也就是4位,執行j-=increment後,不再符合循環條件,循環結束,將”哨兵”temp的值賦給a[j+increment],也就是a[0]的位置,這一步操作就是一個直接插入排序的操作。第一層do-while循環最終的序列如下圖:
我們可以看到,上圖序列中任意位置的關鍵字都符合a[i+4]>a[i],當increment=2的時候也是同樣的道理,細心的同學會發現,不管length是多少,增量increment最終都會等於1,其實,當增量爲1時的操作,就是一次全面的直接插入排序,只不過這時它所操作的序列已經基本有序了。
延伸
既然能將直接插入排序改進爲希爾排序,那冒泡排序能代替直接插入排序嗎?答案是當然的,代碼如下:
void ShellSort(int a[],int length)
{
int i,j;
int increment=length;
do{
increment=increment/2;
for(i=increment;i<length;i++)
{
if(a[i]<a[i-increment])
{
a[i]=a[i]^a[i-increment];
a[i-increment]=a[i]^a[i-increment];
a[i]=a[i]^a[i-increment];
for(j=i-increment;j-increment>=0 && a[j]<a[j-increment];j-=increment)
{
a[j]=a[j]^a[j-increment];
a[j-increment]=a[j]^a[j-increment];
a[j]=a[j]^a[j-increment];
}
}
}
}while(increment>1);
}
注:轉載請說明出處