八大排序算法之直接插入排序(教你用生活的想象,讀懂直接插入算法)
1,生活小游戲:"算法來源於生活",哈哈哈,還記得玩過的抽牌小游戲嗎,你從放在地上的那一堆未知的牌【無序】抽一張牌后,
小腦袋機靈的將抽到的牌放到手中牌【早已被你打理得僅僅有序啦】的某個合適位置后【手中牌保持井井有序】。
手中牌【有序】<-------------------------------- 地上牌【無序】
直接插入排序 == “將牌一張張從地上抽起,然后在手中打理得井井有序”。
2,圖解:
從圖解可以知道咱需要“插入”,而本題咱用的存儲空間是數組~則涉及到了數組的空間移動問題了
~數組移動是從最后一個元素開始,前一個元素的值賦值給后一個元素。
3,簡單分析一下過程:一開始,在整體牌中,有序區的牌只有一張,放在第一個位置,無序區的牌就是從第二張到最后一張結束。
然后,咱不斷的從無序區【從第二張牌開始直到最后一張牌~一個遍歷過程】拿牌插入到有序區的合適位置
【需要通過取出來的牌與已經在有序區的牌比較大小才知道是否合適~也是一個遍歷過程,從有序區的第一個位置開始直到
取出的牌 的前一個位置結束】。
4,所以:外循環(取出無序區的牌):從第二張牌開始到最后一張牌 【無序區的范圍】
內循環(在有序區找合適位置將取出的牌插入):從第一張牌開始直到 取出的牌 的前一張牌 【有序區的范圍】
5,假設,有序區是從小到大排序,則咱因為數組移動空間問題,【有序區的范圍】從最后一個元素開始移動,
於是乎,也從最后該元素比較起,一旦找到在有序第一個數小於取出的數,則有序區的該數后邊便是合適插入位置。
6,比較過程,形象生動用一個想象說明吧,看圖解(咱有一個從小到大的賽道跟一個球),當球停止,便也找到合適位置了:
7,直接上代碼,分析如上:
for(int i = 2; i <= N; i++){ //外循環,從無序區取出牌 arr[0] = arr[i]; //arr[0]是哨兵元素,后邊再補充哨兵元素的好處 for(j = i - 1; arr[0] < arr[j]; j--){ //內循環,從有序區最后一個元素開始比較,知道“球”卡住了,便找到合適位置 arr[j + 1] = arr[j]; } arr[j + 1] = arr[0]; }
ps:代碼優化:【主要是優化在有序區查找那個合適位置,原來咱是從最后一個元素開始比較麻,順帶一起移動元素空間,
優化一下:先找到合適位置,后邊再移動空間~二分(折半)查找優化】
✿✿ 先講清楚在這個有序區找到一個合適位置哈【有序(從小到大)】:
正向思維~咱是從有序區第一個數開始找起,找找找,遇到第一個比【哨兵(待插數)】大的數,
因為從小到大排序,咱知道從這個數開始的后邊的數都會比(哨兵元素)大了,於是合適的位置就是這個數的前面;
逆向思維~咱是從有序區最后一個數開始找起,找找找,遇到第一個比【哨兵(待插數)】小的數,
因為從小到大排序,咱知道從這個數開始的前邊的數都會比(哨兵元素)小了,於是合適的位置就是這個數的后面;
for(int i = 2; i <= N; i++){ //外循環,從無序區取出牌 arr[0] = arr[i]; //arr[0]是哨兵元素,后邊再補充哨兵元素的好處 //二分查找,先在有序區找到那個合適位置先 int low = 1; int hight = i - 1; while(low <= height){ //不斷縮小查找范圍 int mid = (low + height)/2; if(arr[0] < arr[mid]){ height = mid - 1; }else{ low = mid + 1; } } //移動空間 for(int j = i - 1; j >= height + 1; j--){ //內循環,從有序區最后一個元素開始比較,知道“球”卡住了,便找到合適位置 arr[j + 1] = arr[j]; } arr[height + 1] = arr[0]; }
8,哨兵元素好處:參考《數據結構c語言版嚴蔚敏PPT.pdf ~
https://wenku.baidu.com/view/9e73cb8b69dc5022aaea00c1.html》
(1)不用額外增加輔助空間;
(2)省去對下標越界的判斷;