一、從整理撲克牌說起

想象一下,你剛拿到一副打亂的撲克牌,比如:5, 3, 8, 4, 2。現在你想把它們從小到大排好序,應該怎麼做?

最直觀的方法可能是這樣的:先挑出一張牌(比如第一張5)作爲“已排好序的牌堆”,然後依次拿起剩下的牌,每張牌都插入到已排好序的牌堆中合適的位置。

  • 第一步:先理出第一張牌5,已排序牌堆:[5]
  • 第二步:拿起第二張牌3,發現3比5小,所以插入到5前面,已排序牌堆變成:[3, 5]
  • 第三步:拿起第三張牌8,8比5大,直接放到已排序牌堆末尾,變成:[3, 5, 8]
  • 第四步:拿起第四張牌4,需要和已排序牌堆的最後一張牌8比較,4 < 8,所以繼續往前比較5,4 < 5,再比較3,4 > 3,所以插入到3和5之間,已排序牌堆變成:[3, 4, 5, 8]
  • 第五步:拿起第五張牌2,依次和8、5、4、3比較,2比所有牌都小,所以插入到最前面,最終排好序的牌堆:[2, 3, 4, 5, 8]

這個過程是不是很熟悉?其實,這就是插入排序的核心思想——把待排序的元素逐個插入到已排序的序列中

二、插入排序的原理

插入排序的邏輯可以用“逐步構建有序序列”來理解:

  1. 默認有序:假設第一個元素已經是“有序序列”的一部分(只有一個元素,自然有序)。
  2. 逐個插入:從第二個元素開始,把每個元素當作“新牌”,插入到前面已經排好序的序列中的正確位置。
  3. 移動元素,騰出位置:如果新牌比已排序序列中的某些牌小,需要把那些較大的牌“後移”一位,給新牌騰出位置。

三、實例演示:以數組[5, 3, 8, 4, 2]爲例

我們用上面的撲克牌例子,把每個步驟用數組操作來模擬:

步驟 已排序序列 待插入元素 比較過程 插入後序列
1 [5] 3(第二個元素) 3 < 5 → 後移5 [3, 5]
2 [3, 5] 8(第三個元素) 8 > 5 → 直接插入末尾 [3, 5, 8]
3 [3, 5, 8] 4(第四個元素) 4 < 8 → 後移8;4 < 5 → 後移5;4 > 3 → 插入中間 [3, 4, 5, 8]
4 [3, 4, 5, 8] 2(第五個元素) 2 < 8 → 後移8;2 < 5 → 後移5;2 < 4 → 後移4;2 < 3 → 後移3 [2, 3, 4, 5, 8]

關鍵觀察:每次插入時,待插入元素會與已排序序列中的元素從後往前比較,直到找到合適的位置。

四、算法步驟與僞代碼

算法步驟:

  1. 遍歷數組,從第二個元素(索引1)開始處理(記爲i)。
  2. 把當前元素arr[i]暫存爲key(待插入元素)。
  3. 從已排序序列的末尾開始(即i-1),用j指向已排序元素:
    - 如果arr[j] > key,則把arr[j]後移一位(arr[j+1] = arr[j])。
    - 如果arr[j] <= key,則停止移動,此時j+1就是key的插入位置。
  4. key插入到arr[j+1]的位置。
  5. 繼續處理下一個元素(i += 1),直到所有元素處理完畢。

僞代碼:

插入排序(arr)
    對於 i  1  數組長度 - 1:
        key = arr[i]  // 取出待插入元素
        j = i - 1     // 已排序序列的末尾索引
        // 移動已排序元素騰出位置
        while j >= 0  arr[j] > key:
            arr[j + 1] = arr[j]
            j = j - 1
        arr[j + 1] = key  // 插入待插入元素
    返回 arr

五、Python代碼實現

def insertion_sort(arr):
    # 從第二個元素開始遍歷(索引1)
    for i in range(1, len(arr)):
        key = arr[i]  # 待插入元素
        j = i - 1     # 已排序序列的末尾索引

        # 移動已排序元素,直到找到插入位置
        while j >= 0 and key < arr[j]:
            arr[j + 1] = arr[j]  # 已排序元素後移
            j -= 1  # 繼續向前比較
        arr[j + 1] = key  # 插入待插入元素
    return arr

# 測試代碼
test_arr = [5, 3, 8, 4, 2]
sorted_arr = insertion_sort(test_arr)
print("排序前:", test_arr)
print("排序後:", sorted_arr)

六、插入排序的特點

優點:

  • 簡單直觀:邏輯清晰,容易理解和實現。
  • 原地排序:只需要一個臨時變量(key),空間複雜度爲 O(1)。
  • 適應性強:適合“幾乎有序”的數據(比如已排好序的數組,時間複雜度接近 O(n))。
  • 穩定排序:相等元素的相對順序不會改變(如撲克牌中相同點數的牌,原順序保持不變)。

缺點:

  • 時間複雜度高:最壞情況下(完全逆序)爲 O(n²),不適合大規模數據。
  • 移動操作多:對於每個待插入元素,可能需要多次移動已排序元素。

七、總結

插入排序的核心思想就像我們整理撲克牌:先把小範圍的牌排好序,再逐步擴展到整個序列。它的實現邏輯簡單,適合初學者理解排序算法的基本思路,也常用於實際開發中對小規模數據或幾乎有序數據的排序。

如果你覺得插入排序的邏輯還不夠清晰,可以試着用不同的數組(比如 [9, 7, 6, 15, 16, 5])手動模擬排序過程,加深對“移動已排序元素”和“找到插入位置”的理解。排序算法的基礎就是從這些簡單的生活類比開始的~

小夜