数据结构 - 直接插入排序法
數據結構 - 直接插入排序法。
之前的博文已經介紹了 冒泡排序法
和 簡單選擇排序法.
其實上面兩種的基本思路是一樣的, 就是通過兩層循環, 在每1個內循環中找到1個未排序的極值元素, 然后把這個元素移動到數組前列。
但是這篇文章介紹的 直接插入排序法 的基本思路則與上面的排序方法不同, 而且可以講 直接插入排序法 的算法更加直接人類大腦的思路。
基本思路
一個例子
假設我們面前有幾張牌
|1|2|3|7|6|5|9|8|
要讓你移動牌的位置, 讓這些牌從小到達重新排列。
讓我們模擬你的大腦行為…
首先, 我們一樣就看出前面的|1|2|3|是排好序的, 不需要比較和移動。
其次, 我們不會去考慮剩下未排序中的牌哪張是最小的。
而是會去觀察, 咦? 發現6比7大窩~
就去將牌6 放到 7的前面, 牌就變成這樣了:
|1|2|3|6|7|5|9|8|
然后繼續, 又發現5比7小, 就把5放到前面, 放到哪個牌前面呢, 直覺上我們會覺得6比5大, 所以放在6 前面:
|1|2|3|5|6|7|9|8|
下一步, 我們發現9比7大…不移動, 而是把后面的8移動到9前面。
|1|2|3|5|6|7|8|9|
好了, 區區幾步就完成了排序, 是不是覺得我們的大腦很強大?
思路分析
但是, 如果我們要用代碼去模擬我們大腦的算法,就必須了解清楚這個算法的每1個步驟細節。
1.確定開始比較的牌
首先, 我們一開始就跳過了1 2 3 這3張牌, 直接去比較后面那兩張牌7和5。
是因為我們覺得前面的123 已經拍好序了。
但是前面存在若干個排好序的元素是這個排序方法的前置條件嗎?
不是的,
假如我們去掉牌123, 變成如下這樣子:
|7|6|5|9|8|
還是可以用這個思路來排序的。 因為我們可以當第1個元素7, 把它當成1個已經排好序的隊列.
所以, 實際比較是從隊列第二張牌6開始的。
也就是將, 這個思路中, 如果有1個數組 arr[n], 比較從第二個元素arr[1]
2.跟誰比較
接下來繼續講, 我們拿牌6 來 比較了,跟誰比較呢, 是跟6前面的牌7來比較。
也就是講, 如果我們比較的元素是arr[i], 那么會跟它前面的元素arr[i-1]來比較。
3.如果arr[i] 比 arr[i-1] 大
為了方便表述我們把數組改成如下這個狀態
|1|2|3|6|7|5|9|8|
我們正在比較的元素是7, 發現7比6小的情況下, 是不會移動任何牌的位置的, 而是去比較一下張牌5.
也即是將, 如果arr[i] > arr[i-1]的話, 直接i++ 去較下一張牌
4.相反,如果arr[i] 比 arr[i-1] 小
現在我們比較的是5 和 它前一位的7
|1|2|3|6|7|5|9|8|
這種情況下就要改變 元素的位置了。
我們會把牌5往前面放, 但是放在哪里呢, 而且怎么放呢。 還繼續要分析
5. 先把要前移的牌拿出來
隊列就變成這樣子
|1|2|3|6|7|*|9|8|
|5|
6. 前面的牌往后一1個位置。
實際上, 5前面的 6和7 都要往后移動1個位置, 為什么3不移動呢, 因為3比5小啊
分兩步(實際上是1個循環)
|1|2|3|6|7|*|9|8| ==>
|1|2|3|6|*|7|9|8| ==>
|1|2|3|*|6|7|9|8|
7. 拿出來的牌放回去
|1|2|3|5|6|7|9|8|
8. 繼續比較下一張牌
|1|2|3|5|6|7|9|8|
9.小結
可以看出, 這個算法還是有兩個循環的。
外循環用于比較元素和它前1個元素。
內循環實際上是移動1個子隊列。
代碼實現
了解思路后, 用c語言寫下代碼是不難的
int straightInsertionSort(int * arr, int len){int i, j;int tmp = 0;for (int i = 1; i < len; i++){if (arr[i-1] > arr[i]){ //1. the element will be moved only if it's smaller then the previous one//2. the element only will be moved forward.tmp = arr[i]; //use a variable to store the element which will be moved.j = i - 1;do {arr[j + 1] = arr[j]; //move the previous element one position backward fisrt;j--;}while (j >= 0 && arr[j] > tmp); // all the elements after the new position of the moving element move!arr[j + 1] = tmp; //put the moving element to the new position}}return 0; }debug信息
我們來看下 debug信息
4, 1, 5, 8, 0, 3, 7, 9, 6, 2 *1, 4, 5, 8, 0, 3, 7, 9, 6, 2 *0, 1, 4, 5, 8, 3, 7, 9, 6, 2 0, 1, *3, 4, 5, 8, 7, 9, 6, 2 0, 1, 3, 4, 5, *7, 8, 9, 6, 2 0, 1, 3, 4, 5, *6, 7, 8, 9, 2 0, 1, *2, 3, 4, 5, 6, 7, 8, 9 change count: 25 compare count: 22 0, 1, 2, 3, 4, 5, 6, 7, 8, 9可以看出元素是怎樣被移動的。
這個算法比較了20次, 位置移動了25次
首先, 這個算法比較次數相當少, 同1個數組, 用冒泡排序法的比較次數是39哦, 而簡單選擇法的比較次數是45.
位置移動是25, 跟冒泡還多嗎。
不, 冒泡的位置改動實際上是元素交換(移動兩個元素), 而這里的位置改動真的是1個元素的位置改動!
可以看出, 直接插入選擇法比冒泡排序法具有更高的效率。
實際上, 比起簡單選擇法也是更好的
時間復雜度分析。
首先 最好的情況, 就是已經排好序的, 只需要比較n次, 無任何元素移動, 時間復雜度是O(n)
但是, 最壞打情況下的話
就是, 數組本身是按倒序排列的例如
|5|4|3|2|1|
那么, 比較次數是
∑i=1n?1i=1+2+3+4+...+n?1=n(n?1)2
移動次數是
∑i=2n?1i=2+3+4+...+n=n(n+2)2
時間復雜度仍然是O(n2) 哦。
小結
我們又知道了1個更具效率的排序法。
關鍵是,這個直接插入排序法的思路更加符合人腦習慣。
可以看出, 容易寫出的算法(雙重排序法)并不一定是人類大腦最直覺的算法!
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的数据结构 - 直接插入排序法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构 - 简单选择排序法
- 下一篇: powershell 备份文件脚本