排序(二) 十分鐘讓你掌握插入排序
阿新 • • 發佈:2018-12-30
前言:這裡將主要介紹的是直接插入排序和改進後的插入排序也稱為希爾排序
直接插入排序 (時間複雜度為O(N^2))
介紹:
插入排序(Insertion Sort)的演算法描述是一種簡單直觀的排序演算法。它的工作原理是通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,通常採用in-place排序(即只需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,為最新元素提供插入空間。
方式一步驟:
1.從第一個元素開始,該元素可以認為已經被排序
2.取出下一個元素,在已經排序的元素序列中從後向前掃描
3.如果該元素(已排序)大於新元素,將該元素移到下一位置
4.重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5.將新元素插入到該位置中
6.重複步驟2
只有文字的描述還是略顯的不太清晰,老規矩看圖:
實現的原始碼如下:
void InsertSort(T1 * arr, size_t size) //傳入的引數是陣列的首地址,陣列的大小 { assert(arr); for (int i = 1; i < size; ++i) { int end = i - 1; T1 tmp= arr[i]; while (end >= 0 && arr[end] > tmp) //end >= 0 確保的是上圖中的紅色箭頭一直指向陣列的有效地址,arr[end] > tmp 代表箭頭指向的數大於圓圈中的數 { arr[end + 1] = arr[end]; --end; } arr[end + 1] = tmp; } }
方式二:(是在方式一的基礎上進行了一些優化)
思想是這樣子滴:
原始碼如下:
void _Insert(int* arr, int size) { assert(arr); for (int i = 1; i < size; ++i) { for (int end = i - 1; end >= 0; --end) { if (arr[end] > arr[end + 1]) //如果說,發現後面的比前面的小說明需要進行交換了 swap(arr[end], arr[end + 1]); else break; //否則說明 i之前的序列已經是有序的了 } } }
希爾排序:(時間複雜度為 O(N^1.3))
介紹:希爾排序的實質就是分組插入排序,該方法又稱縮小增量排序,因DL.Shell於1959年提出而得名。
基本思想:先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。因為直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,因此希爾排序在時間效率上比前兩種方法有較大提高。
如圖:
原始碼:
void _ShellSort(int * arr, int size) { assert(arr); int gap = size; while (gap > 1) { gap = gap / 3 + 1; //(調整步長)庫中的步長是這樣計算滴,當步長為 1 時其實就是一個直接插入排序 for (int i = gap; i < size; ++i) { int end = i - gap; int tmp = arr[i]; while (end >= 0 && arr[end] > tmp) { arr[end + gap] = arr[end]; end -= gap; } arr[end + gap] = tmp; } } }
//與直接插入排序類似,改進後的希爾排序也可以這樣寫:
void _ShellSort(int * arr, int size) { assert(arr); int gap = size; while (gap > 1) { gap = gap / 3 + 1; for (int i = gap; i < size; ++i) { for (int end = i - gap; end >= 0; end -= gap) { if (arr[end] > arr[end + gap]) //如果不滿足升序,則交換兩個數 { swap(arr[end], arr[end + gap]); } else //否則就認為,i 所在組,在i之前的所有元素都已經是升序了 { break; } } } } }
現在呢,插入排序寫的差不多的,你如果還有什麼更好的想法、意見或者建議的,歡迎參與評論,跪求賜教~~