經典排序算法概括筆記(2)

    上一篇咱們回顧了選擇和冒泡排序、以及改進的冒泡排序兩種算法,今天咱們來看一下插入排序和希爾排序。

插入排序
    插入排序的本質是將待排序序列分紅有序和無序兩部分,一般狀況下咱們都認爲序列的第一元素是有序的,因此插入排序通常是從序列的第二個元素(下標是1的位置)開始。插入排序的的思想是:從無序序列裏取出一個元素,咱們將這個元素叫作哨崗,而後用一個額外的存儲單元將其值保存下來,而後再在有序序列裏,從後向前逐次比較它們和哨崗的大小。若是哨崗比有序序列的值還小,則向後移動有序序列裏的數據,直到找到第一個比哨崗小的元素爲止。仍是之前面用到的序列A={ 6,3,1,9,2,5,8,7,4}爲例,看一下算法的執行過程:
   上述插入排序過程,灰色表示有序部分,白色表示無序部分,黃色表示無序序列裏當前所選擇的哨崗,而橙色表示最後在有序序列爲哨崗所找到的合適位置,綠色的圈表示本趟插入時的執行步驟。簡單分析一下第八趟的插入過程:
   第八趟插入排序開始前,無序序列裏就剩下一個元素了。將元素4標記爲哨崗,用一個額外的存空間temp記錄下。而後從有序序列最後一個元素9開始,與元素4進行比較。由於9比4大,因此元素9向後移動,而後是有序序列裏的元素8,也比4大,一樣地,8也日後移動,一直到元素5也比4大,因此繼續日後移動。到元素3的時候,它比4小,因此元素4應該安放在元素3後面緊挨着的位置,即元素5騰出來的地方,最後將哨崗的值4設置到那裏就OK了。根據上述思路,咱們就能夠很容易地寫出插入排序的核心代碼了:

點擊(此處)摺疊或打開 css

  1. int i,j,tmp;
  2. for(i=1;i<len;i++){     //下標i用於從無須序列裏取元素,由於第一個元素a[0]已經有序,因此下標從1開始
  3.    tmp = a[i];          //tmp表明哨崗,每從無序序列裏取出一個元素a[i],就用哨崗tmp將其值保存下來

  4.    for(j=i;j>0;j--){    //下標j用於在有序序列裏爲哨崗找一個合適的插入位置,j須要在有序序列從後向前遍歷
  5.       if(tmp<a[j-1])
  6.         a[j] = a[j-1];  //將有序序列裏的元素向後移動
  7.       else
  8.         break;          //找到了合適的位置則退出
  9.    }
  10.    a[j] = tmp;          //將哨崗的值安放在合適的位置上
  11. }

    上述代碼的核心邏輯已經很清楚明瞭了,方便理解插入排序的核心思想。這段代碼還能夠寫得更漂亮點:

點擊(此處)摺疊或打開 算法

  1. int i,j,tmp;
  2. for(i=1;i<len;i++){
  3.     for(j=i,tmp=a[i];j>0 && tmp ;j - - ) {
  4.           a[j] = a[j-1];       
  5.     }
  6.    a[j] = tmp;
  7. }
    上面兩段代碼,在gcc標準編譯環境下,後者生成的機器指令代碼確實比前者要少幾條,但在O二、O3優化級別下,兩段程序最終生成的機器指令數量是如出一轍的。備註:我GCC的版本是4.4.7。

   接下來分析一下插入排序算法的效率。若是待排序序列長度爲n,則初始時無序序列長度爲n-1,由於第一個元素是有序的。在最好的狀況下,比較的次數是n-1次,移動元素0次;最壞的狀況是,當序列爲逆序時:
   無須序列第一個元素(即整個待排序序列的第二個元素),比較的次數1,移動1次;
   第二個元素,比較次數2,移動次數2;
   第三個元素,比較次數3,移動次數3;
   ... ...
   第n-1個元素的比較次數爲n-1,移動的次數也是n-1;
   因此,插入排序最壞的狀況下,其時間複雜度n*(n-1)/2,即O(n^2)。
   這裏咱們介紹的是傳統的插入排序,又叫直接插入排序。和現有查找算法進行結合後,產生了新的插入排序算法,例如折半插入排序、表插入排序、希爾插入排序等,這些衍生出來的算法,核心思想和直接插入排序是一致的,變化的部分主要是在有序序列裏找哨崗的位置上進行優化作文章,不像傳統的直接插入排序算法是在有序序列從後向前逐次查找的過程。新算法充分藉助了二分查找,或者希爾查找算法的優點,能夠提升插入排序算法的時間效率。感興趣的朋友能夠本身去寫寫代碼。

希爾排序
   希爾排序又叫遞減增量排序算法,它是直接插入排序的一種改進算法。當增量爲1時,希爾排序就是直接插入排序。若是待排序序列長度爲n,執行第一次希爾排序時,增量通常是n/2;第二次通常是n/4,以此類推,直到增量遞減爲1,再執行一次直接插入排序。繼續看希爾排序的過程, A={ 6,3,1,9,2,5,8,7,4 }:
   上述希爾排序過程當中,前兩趟裏顏色相同的元素屬於一個分組,咱們對每一個這樣的分組使用直接插入排序算法。第三趟時,增量已經爲1了,即進行直接插入排序。因此,基於直接插入排序,咱們能夠很容易寫出希爾排序的核心算法:

點擊(此處)摺疊或打開 ide

  1. int i,j,tmp,d=len;
  2. while((d/=2)>0){      //增量依次遞減到1
  3.   for(i=d;i<len;i++){ //無須序列下標從d開始,且哨崗tmp=a[d]
  4.      for(j=i,tmp=a[i];j>=d && tmp<a[j-d];j-=d){
  5.         a[j] = a[j-d];
  6.      }
  7.      a[j] = tmp;
  8.   }
  9. }

     咱們能夠看到,希爾排序是插入排序更廣泛的狀況。希爾排序算法的重點就在於 增量的選擇方式上,若是增量選擇不合適將會大大影響算法的總體效率,這也是的希爾排序的時間複雜分析起來比較困難的主要緣由。若是按照一般狀況下,待排序列長度折半的方式選取增量的話,其時間複雜度最壞的狀況是O(n^2)。
   欲瞭解更多希爾排序增量的相關知識,請點擊「 這裏」。    未完,待續...
相關文章
相關標籤/搜索