数据结构 - 直接插入排序法
數(shù)據(jù)結(jié)構(gòu) - 直接插入排序法。
之前的博文已經(jīng)介紹了 冒泡排序法
和 簡單選擇排序法.
其實(shí)上面兩種的基本思路是一樣的, 就是通過兩層循環(huán), 在每1個(gè)內(nèi)循環(huán)中找到1個(gè)未排序的極值元素, 然后把這個(gè)元素移動(dòng)到數(shù)組前列。
但是這篇文章介紹的 直接插入排序法 的基本思路則與上面的排序方法不同, 而且可以講 直接插入排序法 的算法更加直接人類大腦的思路。
基本思路
一個(gè)例子
假設(shè)我們面前有幾張牌
|1|2|3|7|6|5|9|8|
要讓你移動(dòng)牌的位置, 讓這些牌從小到達(dá)重新排列。
讓我們模擬你的大腦行為…
首先, 我們一樣就看出前面的|1|2|3|是排好序的, 不需要比較和移動(dòng)。
其次, 我們不會(huì)去考慮剩下未排序中的牌哪張是最小的。
而是會(huì)去觀察, 咦? 發(fā)現(xiàn)6比7大窩~
就去將牌6 放到 7的前面, 牌就變成這樣了:
|1|2|3|6|7|5|9|8|
然后繼續(xù), 又發(fā)現(xiàn)5比7小, 就把5放到前面, 放到哪個(gè)牌前面呢, 直覺上我們會(huì)覺得6比5大, 所以放在6 前面:
|1|2|3|5|6|7|9|8|
下一步, 我們發(fā)現(xiàn)9比7大…不移動(dòng), 而是把后面的8移動(dòng)到9前面。
|1|2|3|5|6|7|8|9|
好了, 區(qū)區(qū)幾步就完成了排序, 是不是覺得我們的大腦很強(qiáng)大?
思路分析
但是, 如果我們要用代碼去模擬我們大腦的算法,就必須了解清楚這個(gè)算法的每1個(gè)步驟細(xì)節(jié)。
1.確定開始比較的牌
首先, 我們一開始就跳過了1 2 3 這3張牌, 直接去比較后面那兩張牌7和5。
是因?yàn)槲覀冇X得前面的123 已經(jīng)拍好序了。
但是前面存在若干個(gè)排好序的元素是這個(gè)排序方法的前置條件嗎?
不是的,
假如我們?nèi)サ襞?23, 變成如下這樣子:
|7|6|5|9|8|
還是可以用這個(gè)思路來排序的。 因?yàn)槲覀兛梢援?dāng)?shù)?個(gè)元素7, 把它當(dāng)成1個(gè)已經(jīng)排好序的隊(duì)列.
所以, 實(shí)際比較是從隊(duì)列第二張牌6開始的。
也就是將, 這個(gè)思路中, 如果有1個(gè)數(shù)組 arr[n], 比較從第二個(gè)元素arr[1]
2.跟誰比較
接下來繼續(xù)講, 我們拿牌6 來 比較了,跟誰比較呢, 是跟6前面的牌7來比較。
也就是講, 如果我們比較的元素是arr[i], 那么會(huì)跟它前面的元素arr[i-1]來比較。
3.如果arr[i] 比 arr[i-1] 大
為了方便表述我們把數(shù)組改成如下這個(gè)狀態(tài)
|1|2|3|6|7|5|9|8|
我們正在比較的元素是7, 發(fā)現(xiàn)7比6小的情況下, 是不會(huì)移動(dòng)任何牌的位置的, 而是去比較一下張牌5.
也即是將, 如果arr[i] > arr[i-1]的話, 直接i++ 去較下一張牌
4.相反,如果arr[i] 比 arr[i-1] 小
現(xiàn)在我們比較的是5 和 它前一位的7
|1|2|3|6|7|5|9|8|
這種情況下就要改變 元素的位置了。
我們會(huì)把牌5往前面放, 但是放在哪里呢, 而且怎么放呢。 還繼續(xù)要分析
5. 先把要前移的牌拿出來
隊(duì)列就變成這樣子
|1|2|3|6|7|*|9|8|
|5|
6. 前面的牌往后一1個(gè)位置。
實(shí)際上, 5前面的 6和7 都要往后移動(dòng)1個(gè)位置, 為什么3不移動(dòng)呢, 因?yàn)?比5小啊
分兩步(實(shí)際上是1個(gè)循環(huán))
|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. 繼續(xù)比較下一張牌
|1|2|3|5|6|7|9|8|
9.小結(jié)
可以看出, 這個(gè)算法還是有兩個(gè)循環(huán)的。
外循環(huán)用于比較元素和它前1個(gè)元素。
內(nèi)循環(huán)實(shí)際上是移動(dòng)1個(gè)子隊(duì)列。
代碼實(shí)現(xiàn)
了解思路后, 用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可以看出元素是怎樣被移動(dòng)的。
這個(gè)算法比較了20次, 位置移動(dòng)了25次
首先, 這個(gè)算法比較次數(shù)相當(dāng)少, 同1個(gè)數(shù)組, 用冒泡排序法的比較次數(shù)是39哦, 而簡單選擇法的比較次數(shù)是45.
位置移動(dòng)是25, 跟冒泡還多嗎。
不, 冒泡的位置改動(dòng)實(shí)際上是元素交換(移動(dòng)兩個(gè)元素), 而這里的位置改動(dòng)真的是1個(gè)元素的位置改動(dòng)!
可以看出, 直接插入選擇法比冒泡排序法具有更高的效率。
實(shí)際上, 比起簡單選擇法也是更好的
時(shí)間復(fù)雜度分析。
首先 最好的情況, 就是已經(jīng)排好序的, 只需要比較n次, 無任何元素移動(dòng), 時(shí)間復(fù)雜度是O(n)
但是, 最壞打情況下的話
就是, 數(shù)組本身是按倒序排列的例如
|5|4|3|2|1|
那么, 比較次數(shù)是
∑i=1n?1i=1+2+3+4+...+n?1=n(n?1)2
移動(dòng)次數(shù)是
∑i=2n?1i=2+3+4+...+n=n(n+2)2
時(shí)間復(fù)雜度仍然是O(n2) 哦。
小結(jié)
我們又知道了1個(gè)更具效率的排序法。
關(guān)鍵是,這個(gè)直接插入排序法的思路更加符合人腦習(xí)慣。
可以看出, 容易寫出的算法(雙重排序法)并不一定是人類大腦最直覺的算法!
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的数据结构 - 直接插入排序法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构 - 简单选择排序法
- 下一篇: powershell 备份文件脚本