1. 程式人生 > >排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現

排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現

排序演算法1——圖解氣泡排序及其實現(三種方法,基於模板及函式指標)
排序演算法2——圖解簡單選擇排序及其實現
排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現
排序演算法4——圖解希爾排序及其實現
排序演算法5——圖解堆排序及其實現
排序演算法6——圖解歸併排序及其遞迴與非遞迴實現
排序演算法7——圖解快速排序以及不同CUTOFF的時間測試


一、直接插入的基本思想

將待排序的一組序列分為已排好序和未排好序的兩個部分
初始狀態時,已排好序序列僅包含第一個元素,未排好序的序列元素為除去第一個以外的n-1個元素
然後,將未排好序序列中的元素逐一插入到已排好序的序列中
如此往復,經過n-1次插入後,未排序序列中的元素個數變為0,排序完成。如下圖所示
在這裡插入圖片描述

程式碼及上下界如圖
在這裡插入圖片描述
從程式碼可以看出,空間複雜度上,簡單插入排序僅需要常數個額外空間
在時間複雜度上,函式中有兩個巢狀的迴圈,每個迴圈進行O(N)次比較和交換,故時間複雜度為O(N^2)
此外,簡單插入排序是穩定的排序,
可以在下面的具體過程中看到,數值相同的兩個記錄不會發生相對位置上的改變

具體過程
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
後面就不列出了


二、折半(二分)插入排序

在這裡插入圖片描述在這裡插入圖片描述
在這裡插入圖片描述

雖然它的時間複雜度也是O(N^2),但由於引用了二分的思想,它的平均效能會比直接插入好

總的來說,插入法比冒泡法和簡單選擇排序法的效能好一些

三、測試結果及程式碼

在這裡插入圖片描述

#include <iostream>

template<class T>
void InsertSort(T *a, int length) {
	T tmp;
	int i, j;
	for (i = 1; i < length; ++i) {
		tmp = a[i];
		for (j = i; j > 0 && a[j - 1] > tmp; --j) {
			a[j] = a[j - 1];
		}
		a[j] = tmp;
	}
}

template<class T>
void BinaryInsertSort(T *a, int length) { int left, right, mid; int tmp; for (int i = 1; i < length; i++) { /* 找到陣列中第一個無序的數,儲存為tmp */ if (a[i] < a[i - 1]) { tmp = a[i]; } else { continue; } /* 找到陣列中第一個無序的數,儲存為tmp */ /* 二分查詢開始 */ left = 0; right = i - 1; while (left <= right) { mid = (left + right) / 2; if (a[mid] > tmp) { right = mid - 1; } else { left = mid + 1; } } /* 二分查詢結束,此時a[left]>=a[i],記錄下left的值 */ /* 將有序陣列中比要插入的數大的數右移 */ for (int j = i; j > left; j--) { a[j] = a[j - 1]; } /* 將有序陣列中比要插入的數大的數右移 */ // 將left位置賦值為要插入的數 a[left] = tmp; } } template<class T> void ArrShow(T *a, int length) { for (int i = 0; i < length; ++i) { std::cout << a[i] << " "; } puts("\n"); } int main(int argc, char *argv[]) { int test[9] = { 9, 1, 5, 8, 3, 7, 4, 6, 2 }; ArrShow(test, 9); puts("InsertSort : "); InsertSort(test, 9); ArrShow(test, 9); int test1[9] = { 9, 1, 5, 8, 3, 7, 4, 6, 2 }; puts("BinaryInsertSort : "); BinaryInsertSort(test1, 9); ArrShow(test1, 9); return 0; }