那些年我们排过的序之希尔排序
基本思想
先取一個小于n的整數d1作為第一個增量,把文件的全部記錄分成d1個組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然后,取第二個增量d2<d1重復上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有記錄放在同一組中進行直接插入排序為止。
該方法實質上是一種分組插入方法引
比較相隔較遠距離(稱為增量)的數,使得數移動時能跨過多個元素,則進行一次比[1]較就可能消除多個元素交換。D.L.shell于1959年在以他名字命名的排序算法中實現了這一思想。算法先將要排序的一組數按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然后再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成一組,排序完成。
一般的初次取序列的一半為增量,以后每次減半,直到增量為1。
排序過程
假設待排序文件有10個記錄,其關鍵字分別是:
四九,三八,六五,九七,七六,一三,二七,四九,五五,零四。
增量序列的取值依次為:
五,三,一[2]
縮小增量
希爾排序屬于插入類排序,是將整個無序列分割成若干小的子序列分別進行插入排序。
排序過程:先取一個正整數d1<n,把所有序號相隔d1的數組元素放一組,組內進行直接插入排序;然后取d2<d1,重復上述分組和排序操作;直至di=1,即所有記錄放進一個組中排序為止。
[3] 三趟結果
零四 一三 二七 三八 四九 四九 五五 六五 七六 九七
Shell排序
Shell排序的算法實現:
1. 不設監視哨的算法描述
void ShellPass(SeqList R,int d)
{//希爾排序中的一趟排序,d為當前增量
for(i=d+1;i<=n;i++) //將R[d+1..n]分別插入各組當前的有序區
if(R[ i ].key<R[i-d].key){
R[0]=R[i];j=i-d; //R[0]只是暫存單元,不是哨兵
do {//查找R的插入位置
R[j+d]=R[j]; //后移記錄
j=j-d; //查找前一記錄
}while(j>0&&R[0].key<R[j].key);
R[j+d]=R[0]; //插入R到正確的位置上
} //endif
優劣
不需要大量的輔助空間,和歸并排序一樣容易實現。希爾排序是基于插入排序的一種算法, 在此算法基礎之上增加了一個新的特性,提高了效率。希爾排序的時間復雜度與增量序列的選取有關,例如希爾增量時間復雜度為O(n2),而Hibbard增量的希爾排序的時間復雜度為O(N(3/2)),但是現今仍然沒有人能找出希爾排序的精確下界。希爾排序沒有快速排序算法快 O(N*(logN)),因此中等大小規模表現良好,對規模非常大的數據排序不是最優選擇。但是比O(N^2)復雜度的算法快得多。并且希爾排序非常容易實現,算法代碼短而簡單。 此外,希爾算法在最壞的情況下和平均情況下執行效率相差不是很多,與此同時快速排序在最壞的情況下執行的效率會非常差。專家們提倡,幾乎任何排序工作在開始時都可以用希爾排序,若在實際使用中證明它不夠快,再改成快速排序這樣更高級的排序算法. 本質上講,希爾排序算法的一種改進,減少了其復制的次數,速度要快很多。 原因是,當N值很大時數據項每一趟排序需要的個數很少,但數據項的距離很長。當N值減小時每一趟需要和動的數據增多,此時已經接近于它們排序后的最終位置。 正是這兩種情況的結合才使希爾排序效率比插入排序高很多。
時間性能
1.增量序列的選擇
Shell排序的執行時間依賴于增量序列。
好的增量序列的共同特征:
① 最后一個增量必須為1;
② 應該盡量避免序列中的值(尤其是相鄰的值)互為倍數的情況。
有人通過大量的實驗,給出了較好的結果:當n較大時,比較和移動的次數約在nl.25到1.6n1.25之間。
2.Shell排序的時間性能優于直接插入排序
希爾排序的時間性能優于直接插入排序的原因:
①當文件初態基本有序時直接插入排序所需的比較和移動次數均較少。
②當n值較小時,n和n2的差別也較小,即直接插入排序的最好時間復雜度O(n)和最壞時間復雜度0(n2)差別不大。
③在希爾排序開始時增量較大,分組較多,每組的記錄數目少,故各組內直接插入較快,后來增量di逐漸縮小,分組數逐漸減少,而各組的記錄數目逐漸增多,但由于已經按di-1作為距離排過序,使文件較接近于有序狀態,所以新的一趟排序過程也較快。
因此,希爾排序在效率上較直接插入排序有較大的改進。
希爾排序是按照不同步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長很小,插入排序對于有序的序列效率很高。所以,希爾排序的時間復雜度會比o(n^2)好一些。由于多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最后其穩定性就會被打亂,所以shell排序是不穩定的。
舉例闡述:
例如,假設有這樣一組數[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我們以步長為5開始進行排序,我們可以通過將這列表放在有5列的表中來更好地描述算法,這樣他們就應該看起來是這樣:
13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10然后我們對每列進行排序:
10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45將上述四行數字,依序接在一起時我們得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].這時10已經移至正確位置了,然后再以3為步長進行排序:
10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45排序之后變為:
10 14 13 25 23 33 27 25 59 39 65 73 45 94 82 94最后以1步長進行排序(此時就是簡單的插入排序了)。
圖示:
C++代碼實現:
?
#include <stdio.h>
?
void? shellsort(int *data, size_t size);
?
?int i, j, temp;
?int gap = 0;
?
int main()
{
???? const int n = 5;
?
???? int a[] = {5, 4, 3, 2, 1};
? shellsort(a,5);
?? /*? while (gap<=n)
???? {
????????? gap = gap * 3 + 1;
???? }
???? while (gap > 0)
???? {
???????? for ( i = gap; i < n; i++ )
???????? {
???????????? j = i - gap;
???????????? temp = a[i];????????????
???????????? while (( j >= 0 ) && ( a[j] > temp ))
???????????? {
???????????????? a[j + gap] = a[j];
???????????????? j = j - gap;
???????????? }
???????????? a[j + gap] = temp;
???????? }
???????? gap = ( gap - 1 ) / 3;
???? } */
? for(i=0;i<5;i++)
???? printf("%d\n",a[i]);
?}
?
void shellsort(int *data, size_t size)
{
?
?int key;
? int j = 0;
???? for (gap = size / 2; gap > 0; gap /= 2)
???????? for ( i = gap; i < size; ++i)
???????? {
?
????????????? key = data[i];
????????????
????????????? for( j = i -gap; j >= 0 && data[j] > key; j -=gap)
????????????? {
???????????????? data[j+gap] = data[j];
?????????????? }?
????????????? data[j+gap] = key;
????????? }
?}
轉載于:https://www.cnblogs.com/hedengfeng/p/3407974.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的那些年我们排过的序之希尔排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ajax调用webService(一)
- 下一篇: 在指定的查找范围内获取DOM元素