一 前言
如果對本系列產生什麼疑問的話, 建議先下前言, 裏面有我的聯繫方式, 教材的下載地址, 特殊詞語的規定之類的........ 鏈接地址: https://blog.csdn.net/qq_41057280/article/details/89209081 ; 最後, 以下說法僅爲個人理解, 如有錯誤, 歡迎教正
二 介紹
插入排序, 全名 直接插入排序算法(Insertion sort), <<算法導論>>原文介紹如下:
我翻譯一下這段話, 插入算法, 就是一個能在一個有序數列中插入一個數,保證插入後此數列仍然有序的排序算法. 適用於數據量級較小的情況.
是不是感覺不太對, 畢竟他的原文和我的翻譯完全不一致. 原文是給大家舉了個例子, 一個拿牌的案例,這個案例生動形象地介紹了插入排序的原理,簡單系統地說明了插入排序基本實現步驟, 能幫助大家快速理解和掌握該算法. 但是, 如果有天要你回答插入算法是什麼, 你總不能跟他說是怎麼怎麼拿牌的吧~~
所以, 在此我就直接忽略原文概括一下.
三 僞代碼
這是<<算法導論>> 提供的插入算法的僞代碼, 由於是第一個僞代碼, 所以就詳細談談該怎麼理解這段僞代碼
- 第一行, for循環, 索引 "j" 從2開始遍歷數組A, 這裏的"2"是指第二個元素
- 第二行, 獲取當前索引 "j" 得到的數組值存與變量key
- 第三行, 註釋, 該行說明不參與運算, 說明數組A的索引是1開始, 而不是0
- 第四行, 得到當前索引的上一個元素索引 "i"
- 第五、六、七行, while倒序循環, 把比key大的已排序的數據後移一位
- 第八行, 將key放入空出來的位置
以上就是插入算法的核心實現思路, 從第二個元素開始插入, 倒序循環並按照排序規則後移元素, 保證位置能空出一位, 直到退出循環, 將元素插入空出來的位置
不清楚怎麼看僞代碼的朋友, 可以看看我寫的僞代碼規則 https://blog.csdn.net/qq_41057280/article/details/90176737
四 java實現
好了, 重頭戲來了. 首先, 先來個常規的照着僞代碼寫.
/**
* 插入排序 , 這裏把變量名儘量與僞代碼統一, 方便大家查看
*/
public void insertSort(int[] A) {
int n = A.length;
for (int j = 1; j < n; j++) {
int key = A[j];
int i = j - 1;
while (i >= 0 && A[i] > key) {
A[i + 1] = A[i]; // 比key大的已排序數據後移一位
i--;
}
A[i + 1] = key; // 空出來的位置,把key放進去
}
}
就把僞代碼換成java代碼即可, 唯一要注意的就是數組索引, 在僞代碼裏面數組下標是從1開始, 而java是從0開始. 所以 while的判斷和 j 的初始值, 都要做點改動
五 基於插入算法實現的數組
按照插入算法的定義, 該算法存在保證數組在插入數據後依舊有序的特性, 如果把該特性運用在數組中, 不就可以實現一個自動排序的數組
代碼如下, 重點在insert方法, 上面插入算法是倒序遍歷, 這次用正序遍歷, 其餘邏輯一致. 之所以換種寫法, 純粹就是閒起來了(滑稽)
class InsertArray{
private int length;
private Integer[] arr;
public InsertArray() {
arr = new Integer[50];
}
public InsertArray(int length) {
arr = new Integer[length];
this.length = length;
}
/**
* 添加數據
*/
public void insert(int value) {
int i;
// 從前往後遍歷, 找到value的位置
for(i = 0; i < length; i++) {
if(arr[i] > value) {
break;
}
}
// 將所有比value大的元素,後移一位
for(int j = length; j > i; j--) {
arr[j] = arr[j - 1];
}
arr[i] = value;
length++;
}
/**
* 重寫toStirng,用於數據展示
* @return
*/
public String toString() {
return Arrays.stream(arr)
.filter(e -> e != null)
.map(e -> e.toString())
.collect(Collectors.joining(","));
}
}
然後測試一下
嗯, 看來是成功了
六 二分插入排序
最後, 我再引申一個插入排序算法的變種, 二分插入排序 . 與直接插入的區別, 就是在搜索數據插入位置, 採用二分法進行搜索, 這樣可以有效的減少比較次數. 僅此而已, 可惜時間複雜度還是O(n²). 代碼複雜, 提升的是無關緊要的效率. 數據量級一大, 該慢還是慢. 評價. 雞肋
/**
* 二分法插入排序
*/
public static void binaryInsertSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int right = i - 1;
int left = 0;
while (left <= right) {
int mid = (left + right) / 2;
if (key < arr[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
for (int j=i; j>left;j--) {
arr[j] = arr[j - 1]; // 比key大的已排序數據後移一位
}
arr[left] = key; // 空出來的位置,把key放進去
}
}
七 總結
插入排序, 是一個很簡單的排序算法. 他在數據量級較小或者數據基本有序的時候推薦使用. 話是這麼說, 其實沒啥人使用(笑~~)