全面分析插入排序的三種插入方式


何謂‘插入排序’? 其概念如是說:每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排序好的序列中,直到全部記錄插入完成為止。

概念的東西總是有些抽象,也可稱其為基本思想。上述插入排序的概念同樣也可說是插入排序的基本思想。抽象的東西理解起來總是有些困難,因此這里需要的是將抽象的概念具體化。

我們不妨將其轉換成整隊問題:開始會有兩隊,其中一隊是按從低到高的順序排列的,將其命名為A隊。另一隊是無序的,將其命名為B隊。如下圖:

 

現在把B隊的第一個人(不妨稱其為jack)放到A隊中,並且保持A隊依然是有序的,為了保持A隊依然有序就需要在A隊中找一個適當的位置給jack,這個位置前面的人要比jack矮,后面的人要比jack高。現在就可以讓jack站到這個位置上面,此時A中依然有序。

 

然后再把B隊的第二個人(稱其為tom)放到A隊,方式和jack相同,要保證A隊依然有序。

 

依次類推直到B隊的人全部站到A隊當中。到此為止,兩隊合成了一隊,而且這一隊是有序的。

 

看到這對插入排序應該有一個比較清晰的認識了。但是這里還存在着一個疑問,排序問題是對一個隊列進行排序,何來的兩隊呢。我們不妨再來轉換一下,起初的時候A、B兩隊站在同一列,並且A隊整體在B隊前面,並且A隊是一個人。對於一個人的隊列肯定是有序的。

 


 

然后再向代碼方面靠近一下,不妨將A、B兩隊映射成數組。有這樣一個數組

 

其中 3 就表示 剛開始的A隊 ,5、2…. 表示剛開始的B隊。而5 就是 Jack, 2 就是Tom。

我當時學習插入排序的時候就是按照這個思路來理解的。到這里我對插入排序的思想基本上已經完全掌握了。

思想的東西轉換成代碼,不同的方式也會產生不同的‘派系’。好比《春秋經》讀起來總是有些難懂,這時就有人出來為春秋作傳,不同的人作出來的傳也是不同的,有《左傳》,有《公羊傳》,有《谷梁傳》 。 雖然有所不同,但是整體都是傳承的《春秋》的思想 。

現在回到我們的插入排序上來,既然思想的東西都已經明了了,無非就是實現方式的差別。關鍵的地方還是在於A隊,當在A隊為B隊的人查找適當位置的時候,查找的方式有很多種。
    1、每次開始從頭遍歷查找位置 稱其為 直接插入排序
    2、用二分法查找位置 稱其為 二分插入排序/折半插入排序

以上兩種方式 都是在一個隊列中查找和移動元素,主要時間花費在查找和移動兩個方面。
還有第三種方式就是借助額外的隊列,來做一個映射,這樣可以省去了移動所花費的時間。
就是用空間換時間的做法,這種方式被稱為:
    3、表插入排序

下面我們分別看一下三種方式的實現代碼

一、直接插入排序

實現步驟:

  1. 將第一個待排序的序列的第一個元素看做一個有序序列,把第二個元素到最后一個元素當成是未排序序列。
  2. 從頭到尾一次掃描未排序的序列,將掃描到的每個元素插入有序序列的適當位置(在這里需要注意一個問題,如果在有序序列中有一個和待插入的元素相等,則將待插入的元素查到此元素的后面,這樣方式的插入排序是穩定的。如果插入到此元素的前面,那么此種方式的插入排序是不穩定的
$arr1 = array(
15,77,23,43,90,87,68,32,11,22,33,99,88,66,44,113,
224,765,980,159,456,7,998,451,96,0,673,82,91,100 ); for($i=1;$i<count($arr1);$i++){ $p = $arr1[$i]; for($j=0;$j<$i;$j++){ if($arr1[$j]>$p){ break; } } for($k=$i-1;$k>=$j;$k--){ $arr1[$k+1] = $arr1[$k]; } $arr1[$j] = $p; }

 

二、 折半插入排序

折半插入排序算法步驟

  1. 將第一個待排序的序列的第一個元素看做一個有序序列,把第二個元素到最后一個元素當成是未排序序列。
  2. 從頭到尾依次掃描未排序的序列,將掃描到的每個元素插入有序序列的適當位置。折半插入排序根據二分查找法在有序序列中查找合適的位置將還未排序的元素插入。(在這里需要注意一個問題,如果在有序序列中有一個和待插入的元素相等,則將待插入的元素查到此元素的后面,這樣方式的插入排序是穩定的。如果插入到此元素的前面,那么此種方式的插入排序是不穩定的)
$arr1 = array(15,77,23,43,90,87,68,32,11,22,33,99,88,66,44,113,224,765
,980,159,456,7,998,451,96,0,673,82,91,100);
for($i=1;$i<count($arr);$i++){ if($arr[$i]<$arr[$i-1]){ //使用二分查找法查找適當的位置 $low = 0; $high = $i-1; $pos = floor(($low+$high)/2); $key = $arr[$i]; while($low<$high){ if($arr[$pos]>$key){ $high = $pos-1; }elseif($arr[$pos]<=$key){ $low = $pos+1; } $pos = floor(($low+$high)/2); } //二分查找法結束 if($arr[$pos]>$arr[$i]){ $pos = $pos-1; } for($j=$i-1;$j>$pos;$j--){ $arr[$j+1]=$arr[$j]; } $arr[$j+1] = $key; } }

 

三、 表插入排序

表插入排序,顧名思義,借助一個索引表對原表進行插入排序,這樣做的好處就是省去了對原來表中元素的移動過程。當然單一的整數數組(僅作為試驗用)移動元素也是挺方便的,但是對於結構有些復雜的表來說,要想移動表中的元素那可真真不是一件容易的事情了。舉個例子(以下PHP中的二維數組)

$link = array();  //鏈表
 
   $link[0]=array('next'=>1);//初始化鏈表  $link第一個元素僅僅作為頭部
 $link[1]=array('next'=>0); //將第一個元素放入$link
/*
  * 開始遍歷數組 從第二個元素開始
  */
 for($i=2;$i<=count($arr);$i++){
     $p = $arr[$i]; //存儲當前待排序的元素
     $index =0; $next = 1; //從開始位置查找鏈表 while($next!=0){ if($arr[$next]['age']<$p['age']){ $index = $next; $next = $link[$next]['next']; } else break; } if($next == 0){ $link[$i]['next'] = 0; $link[$index]['next'] = $i; }else{ $link[$i]['next']=$next; $link[$index]['next']=$i; } }

 

以上是三種插入排序的簡單步驟及實現代碼,詳細的介紹可以參考下面三篇文章

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM