壹 ❀ 引
在前兩篇排序文章中,我們分別介紹了冒泡排序與選擇排序,趁熱打鐵,我們接着聊插入排序。老實說,在分析排序過程中頭腦很清楚,過后再嘗試寫出排序代碼還有點坎坷...可能是我腦瓜子不太機靈的問題,不過我相信多練習對於算法的感覺會好起來,那么讓我們開始。
貳 ❀ 插入排序基本概念
插入排序與我們生活中打撲克牌斗地主的原理相同,在分完手牌后,拿起來就是一堆亂序的牌組,接下來我們總是會一一把排序不對的牌抽出來,並插入到它應該在的位置,那么這個調整牌組順序的過程就是在做插入排序。只是打撲克時,我們總是會站在宏觀視角,以最便捷的方式把牌調整好,而程序不同,它得按照一個邏輯一步步調整,大家明白這個意思就好。

插入排序主要按照如下步驟運轉:
- 將數組中的第一個元素看成有序序列,也就是最初的參照點,把從第二個元素到尾部所有元素都看成未排序序列。
- 開始遍歷未排序序列,將每個未排序元素插入到有序序列中它應該的位置,如果未排序元素與有序序列中某個元素相同,則將其放到有序序列該元素后面。
我們來看個簡單的例子,假設現在有數組[4,2,1,3]
;
現在4是有序序列,2,1,3為無序序列,開始遍歷。
- 無序序列第1次遍歷
- 有序序列第1次遍歷
- 2與4比較,因為2更小,2與4互換位置,此時有序序列為
2,4
,無序序列為1,3
。
- 2與4比較,因為2更小,2與4互換位置,此時有序序列為
- 有序序列第1次遍歷
- 無序序列第2次遍歷
- 有序序列第1次遍歷
- 1比4小,互換位置,此時有序序列為
2,1,4
,無序序列為3
。
- 1比4小,互換位置,此時有序序列為
- 有序序列第2次遍歷
- 1比2小,互換位置,此時有序序列為
1,2,4
,無序序列為3
。
- 1比2小,互換位置,此時有序序列為
- 有序序列第1次遍歷
- 無序序列第3次遍歷
- 有序序列第1次遍歷
- 3比4小,互換位置,此時有序序列為
1,2,3,4
,排序完成。
- 3比4小,互換位置,此時有序序列為
- 有序序列第1次遍歷
通過上述分析,我們大致可以得到這樣幾個結論:
- 數組遍歷必定是從i=1處開始。
- 每次遍歷我們都會拿一個無序序列的元素和有序序列元素進行比較,有序序列可能存在多個,所以需要循環嵌套。
知道了這些,我們來嘗試實現代碼。
叄 ❀ 插入排序實現
function insertionSort(arr) {
var len = arr.length,
preIndex;
// 無序序列從i=1處開始遍歷
for (var i = 1; i < len; i++) {
// 記錄上一個值的索引
preIndex = i - 1;
// 如果上一個值比當前值大,互換位置,考慮有序序列可能有多個,所以需要讓preIndex遞減
while (preIndex >= 0 && arr[preIndex] > arr[preIndex + 1]) {
arr[preIndex] = [arr[preIndex + 1], arr[preIndex + 1] = arr[preIndex]][0];
preIndex--;
};
};
return arr;
};
var arr = [4, 2, 1, 3];
insertionSort(arr); //[1, 2, 3, 4]
當然我這個實現看着可能不太好看,不過我個人感覺更好理解一點,下面是偏官方的插入排序實現:
function insertionSort(arr) {
var len = arr.length;
var preIndex, current;
for (var i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
};
arr[preIndex + 1] = current;
};
return arr;
};
與我的實現不同地方在於,它不是每次對比后直接交換位置,而是用當前值一直和上一個比,滿足條件就修改當前值,由於preIndex
一直遞減,所以當前值一直在變,一直比完為止,最后設置數組0項為當前值,而此時的當前值一定是有序序列中最小的一個。比較巧妙,不過從記憶角度上來說,我可能有些記不住....因人而異了。
那么關於插入排序就說到這里,文中如果存在描述不當還望指出,本文結束。