希爾排序原理(java實現)
希爾排序也是排序演算法的一種,先說他的定義,希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,演算法便終止。(摘自百度百科)
看不懂,對吧,我用白話文說一說,其實他就是一個改良版的插入排序(插入排序可以參考我以前的部落格),為什麼這麼說呢,如果你仔細去研究插入排序演算法,很快就會發現,這種演算法的效率與初始資料的狀態有關,假如初始資料本來就是有序的,那麼,排序將相當快,初始資料基本上有序時,簡單的插入排序也有很高的效率,希爾排序,就是基於這個事實,設計出來的更高效的演算法,他是這樣的原理,首先定義一個步長(也就是增量),通過這個步長將原始序列劃分成多個子序列,並將這些子序列進行簡單插入排序,然後再選一個較小的步長,再將這個原始序列劃分成多個子序列,再針對這些子序列進行簡單插入排序,直到最後,步長不能再小了,也就是步長為1了,再進行簡單插入排序之後,整個序列就變成有序序列了
其實,步長為1的時候進行簡單插入排序,就是真正的簡單插入排序,只不過,像上面說的,這個資料的初始狀態已經是基本上是有序了,所以就說,希爾排序是簡單插入的改良版
如果你看到這裡,還是不懂的話,沒關係,來舉個例子,假定現在有這樣一個整型陣列{1,5,2,7,3,4,6,9,8,10},首先,選定一個步長,這個步長的選定是很有講究的,不過這裡我們暫且不說這個,後面我會提,就比如現在我們選定步長為3,然後,怎麼就能將原始序列劃分成多個數列了呢?是這樣的,先把第1個數拿出來,然後,1+3,就是第4個數,1+2x3,就是第7個數,1+3x3就是第10個數,1+4x3,就是第13個數,但是,出界了,那麼就不往後看了,所以,這個子序列就是{1,7,6,10},然後再拿出第2個數,2+3,就是第5個數,2+2x3就是第8個數,2+3x3就是第11個數,但是第11個數不存在,那麼第二個子序列就是{5,3,9},再拿出第3個數,3+3,第6個數,3+2x3,第9個數,3+3x3,第12個數,出界,那麼這個子序列就是{2,4,8},這樣我們就選擇出了這四個子序列,分別對這四個子序列進行簡單插入排序,然後這四個序列就分別都是有序的了,原序列就變成了{1,3,2,6,5,4,7,9,8,10},讀者可以自己拿張紙,自己排一下,就明白了,然後再將步數變成2,經歷上面的同樣的過程,最後再將步數變成1,再經歷上面同樣的過程,那麼,整個序列就有序了,讀者可以看著下面的程式碼,自己走一走步驟,就明白了
下面是Java程式碼實現
class Demo
{
public static void main(String[] args)
{
//定義整型陣列
int[] arr = {1,5,2,7,3,4,6,9,8,10};
//呼叫希爾排序函式
Shell(arr);
//輸出排序後的陣列
for(int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
}
//定義希爾排序函式
public static void Shell(int[] arr)
{
//dk是步長
int dk = arr.length;
while(dk!=1)
{
//剛開始選擇長度的一半作為步長,每次減少一半
dk = dk/2;
//k是每個子序列的第一個元素的下標
for(int k=0;k<=dk;k++)
{
//通過改變i來改變倍數,確定下標
for (int i=1;k+i*dk<arr.length;i++)
{
//j是子序列中,小於i的所有下標
for(int j=0;j<i;j++)
{
//子序列進行插入排序
if(arr[k+j*dk]>arr[k+i*dk])
{
int tmp = arr[k+i*dk];
for(int p=i;p>j;p--)
arr[k+p*dk] = arr[k+(p-1)*dk];
arr[k+j*dk] = tmp;
}
}
}
}
}
}
}
不太好理解,慢慢來,然後在這裡說一下關於步長的選擇問題,那是數學家研究的問題,有興趣的讀者可以去相關網站研究