1.冒泡排序算法
要說冒泡應該是很是簡單的一種排序了,思路就如其名,數據像是泡泡同樣逐個上升。shell
/* * 冒泡排序 */ void bubbleSort(int *array , int length) { //設置flag來標示是否已經有序,用於優化冒泡排序 int flag; //共執行n-1趟 for(int i = 0 ; i < length - 1; i++) { //默認爲已經有序 flag = 0; //從最後一個元素開始判斷,由於每一趟都會排好一個元素,因此每一趟都會少比較一次 for(int j = length - 1 ; j > i ; j--) { //比較是否比前面的元素小 if(array[j-1] > array[j]) { //若是小就交換位置而且標示爲非有序數組 swip(&array[j-1], &array[j]); flag = 1; } } //若是已經默認有序就不在進行 if(!flag) break; } }
這裏說一下標示flag,若是已經有序好比一、二、三、四、五、6數組,可是內層循環依然會執行,只是不交換元素而已。當執行到沒有元素交換的時候也就說明該數組已經有序了,這時就能夠退出循環。數組
2.簡單選擇排序優化
簡單選擇排序就是先找當前位置爲最小,判斷後面是否有比當前元素小的,若是存在就和當前元素交換位置。相比冒泡排序簡單選擇的比較次數較多而交換最多就n-1次。ui
/* * 簡單選擇排序 */ void selectSort(int *array, int length) { //和冒泡排序同樣,也是執行n-1次 for(int i = 0 ; i < length-1 ; i++) { //猜想當前位置爲最小元素 int min = i; //執行n-i-1次,用於找到最小的元素 for(int j = i+1 ; j < length ;j++) { //若是當前元素比min位置小,那麼當前位置爲min if(array[min] > array[j]) min = j; } //若是min的位置發生變化,即當前位置不是min,那麼交換位置 if(min != i) swip(&array[min], &array[i]); } }
3. 直接插入排序spa
直接插入排序思路也很簡單,就很少說了。code
/* * 直接插入排序 */ void insertSort(int *array ,int length) { //有幾個元素就執行幾回,默認最少兩個元素,從第二個元素開始 for(int i = 1 ; i < length ; i++) { //若是當前位置比前一位置元素小 if(array[i] < array[i-1]) { //保存當前元素 int temp = array[i]; int j; //當前元素前面的全部比當前元素大的元素所有後移一位 for(j = i-1 ; array[j] > temp && j >= 0; j--) { array[j+1] = array[j]; } //把空出來的位置賦上保存的元素 array[j+1] = temp; } } }
4.希爾排序blog
希爾排序就是直接插入排序的一個升級,讓序列先相對有序,而後在不斷減少間隔從新分組使整個有序。排序
/* * 希爾排序 */ void shellSort(int *array ,int length) { //設置希爾排序的間隔(每次/2) int increment = length/2; //當間隔變爲0時結束 while(increment >= 1) { //從第一個間隔位置開始,到最後,分好組對每一組進行直接插入排序 for(int i = increment ; i < length ; i++) { //和直接插入排序相同,只是間隔從直接插入的1變爲了increment if(array[i] < array[i-increment]) { int j; int temp = array[i]; for(j = i - increment ; array[j] > temp && j >= 0 ; j -= increment) { array[j+increment] = array[j]; } array[j+increment] = temp; } } //更新間隔 increment /= 2; } }
5.堆排序遞歸
堆排序相對複雜一些,主要思路就是把線性表當作徹底二叉樹去處理,而後構造大頂堆。
/* * 調整堆結構 */ void heepAdjust(int *array, int loc , int length) { //保存當前位置數據 int temp = array[loc]; //獲得左子節點位置,每次都獲得子節點 for(int i = loc * 2 ; i <= length ; i *= 2) { //若是當前位置<長度,就說明父節點有兩個子節點,若是等於長度就只有一個 //若是有兩個,把位置調整到較大的位置 if(i < length && array[i] < array[i+1]) i++; //判斷父節點是否比較大的大 if(temp > array[i]) break; //把較大的賦值給父節點 array[loc] = array[i]; //移動父節點位置到當前節點 loc = i; } //最終位置賦值爲保存的數據 array[loc] = temp; } /* * 堆排序 * array 中的0號元素空過去,因此length是array長度-1 */ void heepSort(int *array , int length) { //長度/2獲得最後一個有子節點的節點位置,循環到根節點 for(int i = length / 2 ; i > 0 ; i--) { //調整堆結構,始終保持大頂堆 heepAdjust(array, i, length); } //從最後一個元素開始,逐個和根節點交換並調整堆結構 for(int i = length ; i > 1 ; i--) { swip(&array[1], &array[i]); heepAdjust(array, 1, i-1); } }
6.歸併排序
歸併排序應該算是最很差理解的了,歸併排序分爲拆分遞歸和合並回朔兩個過程,其中在拆分的時候會創建一個數組用於保存下一層的回朔。
遞歸實現:
/* * 合併排序當前層的數據 */ void Merge(int SR[], int TR[], int l , int m , int r) { //左邊的初始位置 int i = l; //右邊的初始位置 int j = m+1; //TR的位置 int k = l; //若是比較排序沒有完成就循環 while(i <= m && j <= r) { //由於左右相對有序,因此只須要每次找左邊和右邊較小的,而後讓找的位置+1 if(SR[i] < SR[j]) { TR[k] = SR[i]; i++; } else { TR[k] = SR[j]; j++; } //保存的數組位置+1 k++; } //把剩下沒有歸併的數據所有存入TR if(i <= m) { for(int n = 0 ; n <= m - i ; n++) { TR[k] = SR[i+n]; k++; } } if(j <= r) { for(int n = 0; n <= r - j ; n++) { TR[k] = SR[j+n]; k++; } } } /* * 遞歸歸併過程 */ void MSort(int SR[], int TR1[] ,int l , int r) { //定義一個數組用來存放該層歸併的結果 int TR2[20] = {0}; //若是已經不能在拆分,把數據賦值給TR1回朔,注意這裏TR1回朔後就是上一層的TR2了 if(l == r) { TR1[l] = SR[l]; } else { //歸併的界限計算 int m = (l+r)/2; //拆分左右,TR2是回朔合併用的 MSort(SR, TR2, l, m); MSort(SR, TR2, m+1, r); //把該層的TR2從新排序並賦值給上一層的TR2 Merge(TR2 ,TR1, l, m, r); } } void MergeSort(int *array, int length) { MSort(array, array, 0 ,length - 1); }
非遞歸實現:Merge方法和遞歸版的同樣
void MergePass(int SR[], int TR[], int k, int length) { int i; //判斷是否還有間隔能容納數據,也就是判斷當最後不夠兩組的時候結束 for(i = 0 ; i < length-2*k+1 ; i+=2*k) { //合併 Merge(SR, TR, i, i+k-1, i+2*k-1); } //判斷是否是在1組合2組數據之間 if(i < length - k) { Merge(SR, TR, i, i+k-1, length-1); } //1組數據如下或者間隔超過了數組長度 else { for(int j = i ; j < length ;j++) { TR[j] = SR[j]; } } print(TR,length); } /* * 歸併排序(非遞歸) */ void MergeSort(int *array , int length) { //開闢一個和數據同樣大的空間 int *TR = (int *)malloc(sizeof(int) * length); //初始合併間隔爲1 int k = 1; //k < length就不斷地合併 while (k < length) { //把array合併到TR MergePass(array,TR,k,length); //間隔*2 k *= 2; //把TR合併回來 MergePass(TR, array, k, length); //間隔*2 k *= 2; } }
7.快速排序
快速排序是冒泡排序的一個升級,經過不斷調整元素位置來排序,是排序算法中最爲高效的
int Partition(int *array , int low , int high) { //取第一個元素做爲目標位置 int pivotKey = array[low]; while (low < high) { //若是high比目標小,交換 while (low < high && array[high] > pivotKey) { high--; } swip(&array[low], &array[high]); //若是low比目標大,交換 while (low < high && array[low] < pivotKey) { low++; } swip(&array[low], &array[high]); } return low; } void Qsort(int *array , int low, int high) { //目標值的位置 int pivot; if(low < high) { //返回目標值位置 pivot = Partition(array ,low ,high); //對前面進行快速排序 Qsort(array, low, pivot-1); //對後面進行快速排序 Qsort(array, pivot+1, high); } } void QuickSort(int *array , int length) { Qsort(array,0,length-1); }
總結
插入排序類 | 直接插入排序 | 希爾排序 |
選擇排序類 | 簡單選擇排序 | 堆排序 |
交換排序類 | 冒泡排序 | 快速排序 |
歸併排序類 | 歸併排序 |
排序算法指標
排序方法 | 平均狀況 | 最好狀況 | 最壞狀況 | 輔助空間 | 穩定性 |
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 穩定 |
簡單選擇排序 | O(n2) | O(n2) | O(n2) | O(1) | 穩定 |
直接插入排序 | O(n2) | O(n2) | O(n2) | O(1) | 穩定 |
希爾排序 | O(nlogn)-o(n2) | O(n1.3) | O(n2) | O(1) | 不穩定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不穩定 |
歸併排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 穩定 |
快速排序 | O(nlogn) | O(nllogn) | O(n2) | O(logn)~O(n) | 不穩定 |