一.直接(選擇)插入排序
有兩種方式:升序和降序 我使用升序
直接(簡單)插入排序:
每次向已經排序好的隊列里面找個合適的位置,將值插入
//筆試和面試:
//1.算法的描述 2.算法的實現 3.效率(時間復雜度和空間復雜度和穩定性)
//穩定性定義:如果兩個關鍵值A和A`,如果一開始A就在A`前面,你排序后A還在A`前面,我們就認為
是穩定的
//怎么看穩定性:看有沒有跳躍交換
直接插入排序:
如果數組基本有序,我們就用直接插入排序,越有序,時間復雜度越小,極端情況下為O(n)
時間復雜度O(n^2) 空間復雜度O(1) ,穩定的
為什么不用從前向后找:如果數組有序,則時間復雜度太大
具體代碼實現:
#include <stdio.h> #include <assert.h> void InsertSort(int arr[], int len) { //循環多少次 個數-1 //用臨時量tmp保存關鍵值,從后向前找,比它小的或者走到了頭,就將關鍵值放到下一個位置上 assert(arr != NULL); if (NULL == arr) return; int count = 0; int tmp = 0; int j = 0; for (int i = 1; i < len; i++)//控制揭牌后需要排序的次數 { tmp = arr[i]; for (j = i - 1; j >= 0; j--)//從后向前找 { if (arr[j] > tmp)//比關鍵值大,則向后移動 { arr[j + 1] = arr[j]; count++; } else { break;//找到了比它小的值 退出 } } arr[j + 1] = tmp; } printf("count %d\n", count); } void Show(int* arr, int len) { assert(arr != NULL); if (NULL == arr) return; for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 }; InsertSort(arr, sizeof(arr) / sizeof(arr[0])); Show(arr, sizeof(arr) / sizeof(arr[0])); return 0; }
希爾shell排序:
就是一種特殊的直接插入排序,只不過調用了很多次直接插入排序,
按增量分組
要求:增量最后一個必須為1, 增量保持互素
時間復雜度O(n^1.3~1.5),空間復雜度O(1) ,穩定性:發生了跳躍交換,所以不穩定

例如:
分成5組,處理之后的值:

分成3組,處理之后的值:

最后分成1組,處理之后的值:

具體代碼實現:
#include <stdio.h> #include <assert.h> static void Shell(int arr[], int len, int gap)//gap 分成多少組(間隔) { int tmp = 0; int j = 0; int count = 0; for (int i = gap; i < len; i++)//i開始的位置 { tmp = arr[i]; for (j = i - gap; j >= 0; j = j - gap) { if (arr[j] > tmp) { arr[j + gap] = arr[j]; count++; } else { break; } } arr[j + gap] = tmp; } printf("%d count %d\n", gap, count); } void ShellSort(int arr[], int len) { assert(arr != nullptr); if (NULL == arr) return; int dk[] = { 5, 3, 1 }; for (int i = 0; i < sizeof(dk) / sizeof(dk[0]); i++) { Shell(arr, len, dk[i]); } } void Show(int* arr, int len) { assert(arr != NULL); if (NULL == arr) return; for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr2[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 }; ShellSort(arr2, sizeof(arr2) / sizeof(arr2[0])); Show(arr2, sizeof(arr2) / sizeof(arr2[0])); return 0; }
二.交換排序
冒泡(沉石)排序:
兩兩比較,大的向后挪,小的向前挪
時間復雜度O(n^2) 空間復雜度O(1) 穩定的
具體代碼實現:
//冒泡排序:兩兩比較, 大的向后挪 void BubbleSort(int arr[], int len) { //assert /*int count=0; bool tag = true;*/ int tmp = 0; for(int i=0; i<len-1; i++)//次數 { //tag = true; for(int j=0;j+1<len-i; j++)//兩兩比較,大的向后挪 { if(arr[j] > arr[j+1]) { tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; //tag = false; } //count++; } /*if(tag) { break; }*/ } //printf("count = %d\n", count); }
int main()
{
int arr[] = {2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88}; BubbleSort(arr, sizeof(arr)/sizeof(arr[0])); Show(arr, sizeof(arr)/sizeof(arr[0])); return 0; }
快速排序法
每次找到基准值,以基准值為分界線,將數據分成兩塊,左邊的數據都比基准值小,右邊的數據都比基准值大快速排序,越有序越慢,
規則:
1.從右向左找比基准值小的數據,找到后,向左挪動
2.從左向右找比基准值大的數據,找到后,向右挪動
3.重復1,2,直到left == right,結束,將基准值放到arr[left] 或者arr[right]內
缺點:越有序越慢,不穩定

具體實現代碼:
#include<stdio.h> #include<assert.h> static int Partition(int arr[], int left, int right) { int tmp = arr[left]; while (left < right)//進來保證有兩個值 { while(left < right && arr[right] > tmp) right--; if(left == right) { break; } arr[left] = arr[right]; while(left < right && arr[left] <= tmp) left++; if(left == right) { break; } arr[right] = arr[left]; } arr[left] = tmp;//arr[right] = tmp; return left;//return right; 因為此時left == right } static void Quick(int arr[], int left, int right) { if(left < right) { //第一種優化:如果有效個數特別少,直接調用直接插入排序 /* if(right-left+1<20 ) //自己設置有效個數 { Insertsort(arr,left, high) return; } // Insertsirt表示一個插入排序類 */ //第二種優化:三數取中 /* GetMiddleNumber(arr,left,mid,right); */ //第三種優化:防止完全有序,自己打亂一下 /* Swap(arr,start,rand()%(right-left+1)+start; */ //第四種 /*if(left < right) { int midindex = Partition(arr, left, right); if(left < midindex-1) { Quick(arr, left, midindex-1); } if(midindex+1 < right) { Quick(arr, midindex+1, right); } }*/ int midindex = Partition(arr, left, right); Quick(arr, left, midindex-1); Quick(arr, midindex+1, right); } } //用棧 static void Quick_stack(int arr[], int left, int right) { stack<int> st; if (left < right) { int midindex = Partition(arr, left, right); if (left < midindex - 1) { st.push(left); st.push(midindex - 1); } if (midindex + 1 < right) { st.push(midindex + 1); st.push(right); } } while (!st.empty()) { int q = st.top(); st.pop(); int p = st.top(); st.pop(); int midindex = Partition(arr, p, q); if (p < midindex - 1) { st.push(p); st.push(midindex - 1); } if (midindex + 1 < q) { st.push(midindex + 1); st.push(q); } } } void QuickSort(int arr[], int len)//時間復雜度O(nlogn) 空間復雜度O(1) 不穩定 { //assert Quick_stack(arr, 0, len-1); }
第一種優化代碼:
void InsertSort(int arr[], int left, int right) { int tmp = arr[left]; for (int i = left + 1; i <= right; i++) { tmp = arr[i]; int j = i - 1; while (j >= right && arr[i] > tmp) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = tmp; } }
第二種優化代碼:
void GetMiddleNumber(int arr[], int left, int right) { if (arr[left] > arr[right]) { Swap(arr, left, right);//交換左右端數據,保證左端較小 } if (arr[mid] > arr[right]) { Swap(arr, left, right);//交換中間和右邊,保證中間較小 } if (arr[mid] > arr[left]) { Swap(arr, left, right);//交換中間和左端數據,保證左邊不是最小的那一個 } }
第三種優化代碼:
Swap(arr, left, rand() % (end - start + 1) + start);//取一個小於有效長度隨機值+最左端值的下標作為隨機基准值的下標與start進行交換
三.選擇排序
直接選擇(簡單選擇排序):
每次從待排序隊列中找到最小值,和待排序隊列的第一位交換即可
時間復雜度O(n^2) 空間復雜度O(1) 不穩定的

具體實現代碼:
#include<stdio.h> #include<assert.h> void SelectSort(int arr[], int len) { assert(arr != NULL); if (NULL == NULL)return; int tmp = 0; for (int i = 0; i < len - 1; i++) { int m= i;//存放最小值下標 for (int j = i + 1; j < len ; j++) { if (arr[j] <arr[m]) { m = j; } } if (m != i)//if判斷可省略 { tmp = arr[m]; arr[m] = arr[i]; arr[i] = tmp; } } } void Show(int* arr, int len) { assert(arr != NULL); if (NULL == arr) return; for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 }; SelectSort(arr, sizeof(arr) / sizeof(arr[0])); Show(arr, sizeof(arr) / sizeof(arr[0])); return 0; }
堆排序:
堆排序的時間復雜度O(nlogn) 空間復雜度O(1) 不穩定
什么是堆?
堆分為兩種:大頂堆和小頂堆 兩個統稱為堆
大頂堆:一個二叉樹,父節點的值大於子節點的值
小頂堆:一個二叉樹,父節點的值小於子節點的值
什么是樹形結構:
二叉樹,樹根,深度,葉子結點,左孩子,右孩子,完全二叉樹,滿二叉樹
深度怎么求:log2n+1
大頂堆和小頂堆的關系,和兄弟節點的值無關,只和父子節
點有關
調整2個要點:1.從最后一個非葉子節點子樹開始從后向前調整
2.調整的時候順序是從上向下
3.升序(大頂堆),降序(小頂堆)
具體實現代碼:
#include<stdio.h> #include<assert.h> static void HeapAdjust(int arr[], int start, int end)//時間復雜度O(log2n) 空間 復雜度O(1) { int tmp = arr[start]; for(int i=2*start+1; i<=end; i=i*2+1)//i? 堆排序效率高體現在這里i=i*2+1 { //1.左右孩子都存在 //2.只有左孩子,沒有右孩子 if(i<end && arr[i] < arr[i+1])//通過i<end保證右孩子存在,且arr[i] <arr[i+1]保證左孩子小於右孩子 { i++;//這時候讓i指向較大的右孩子下標 } //if判斷失敗的話,要么沒有右孩子,要么有右孩子但是左孩子比右孩子值大,所以i不需要 改變 if(arr[i] > tmp)//判斷較大孩子節點的值是否比父節點的值大,大的話向上覆蓋,不然 就找到了合適位置 { arr[start] = arr[i]; start = i; } else { break;//左右孩子中較大的孩子值小於tmp } } arr[start] = tmp;//有兩種情況執行到這一行代碼:1.觸底 2.找到放tmp的合適位置 } //堆排序的時間復雜度O(nlog2n) 空間復雜度O(1) 不穩定 void HeapSort(int arr[], int len) { //assert //2.調整為大頂堆 for(int i=(len-1-1)/2; i>=0; i--)//O(nlog2n) { HeapAdjust(arr, i, len-1);// } //第一個for循環走出來,這時已經為大頂堆了 int tmp = 0; for(int j=0; j<len-1; j++)//j指的是循環的次數(頂部數據和最后一個節點交換的次 數)//O(nlog2n) { //3.將頂部數據和最后一個節點進行了交換 tmp = arr[0]; arr[0] = arr[len-1-j]; arr[len-1-j] = tmp;//已經將頂部數據和最后一個節點進行了交換 //4.重復2.3操作 HeapAdjust(arr, 0, (len-1-j)-1); } } void Show(int* arr, int len) { assert(arr != NULL); if (NULL == arr) return; for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 }; HeapSort(arr, sizeof(arr) / sizeof(arr[0])); Show(arr, sizeof(arr) / sizeof(arr[0])); return 0; }
四.二路歸並排序
二路歸並排序,非遞歸形式:
將兩個有序的段合並成一個有序的段,直到全部數據在同一個段內有序,則完成有序
時間復雜度O(n log2n) 空間復雜度O(1) 穩定的
具體代碼實現:
//一次歸並 以gapgap合並 static void Merge(int arr[], int len, int gap)//gap 標志幾幾合並 { int *brr = (int*)malloc(sizeof(int) * len); assert(brr != NULL); int low1 = 0; int high1 = low1 + gap -1; int low2 = high1 + 1; int high2 = low2+gap-1<len ? low2+gap-1 : len-1;//H2 有可能越界 若小於則low2+gap-1,不是則len-1 int i = 0; while(low2 < len)//有兩個有序段 { while(low1 <= high1 && low2 <= high2)//兩個段內頭指針都沒走到尾巴 { if(arr[low1] <= arr[low2]) { brr[i++] = arr[low1++]; } else { brr[i++] = arr[low2++]; } } //左邊的段走到尾,那直接將右邊的段內數據向下拷貝到brr內即可 while(low2 <= high2) { brr[i++] = arr[low2++]; } //右邊的段走到尾,那直接將左邊的段內數據向下拷貝到brr內即可 while(low1 <= high1) { brr[i++] = arr[low1++]; } //更改L1L2 H1H1,讓指向接下來的兩個有序段即可 low1 = high2 + 1; high1 = low1+gap-1; low2 = high1 + 1; high2 = low2+gap-1<len ? low2+gap-1 : len-1; } //只有一個有序段 while(low1 < len) { brr[i++] = arr[low1++]; } //將brr里的全部值拷貝到arr里面,然后將brr釋放 for(int j=0; j<len; j++) { arr[j] = brr[j]; } free(brr); brr = NULL; } void MergeSort(int arr[], int len)//控制合並次數 { assert(arr != NULL); if(NULL == arr) return; for(int i=1; i<len; i*=2) { Merge(arr, len, i); } } int main() { int arr[] = {2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88}; MergeSort(arr, sizeof(arr)/sizeof(arr[0])); Show(arr, sizeof(arr)/sizeof(arr[0])); return 0; }
五.基數排序
又稱桶排序
低位優先,所有數據從低位(個)位開始,依次放入到對應的十個桶內(隊列中),再依次從桶中將數據依次取出(出隊),直到所有數據全部有序。
循環次數和最大位數有關
時間復雜度o(n) , 空間復雜度o(n)
此時完全有序
具體實現代碼:
#include<stdio.h> #include<assert.h> //基數排序 //獲取數組中最大值的位數 static int Get_Figure(int arr[], int len) { int max = 0; for(int i=0; i<len; i++) { if(arr[i] > max) { max = arr[i]; } } int count = 0; while(max != 0) { count++; max /= 10; } return count; } //獲取n的第fin位的值 //1234,2 = 2 //234,0 = 4 //12345,4 = 1 //12345,7 = 0 static int Get_num(int n, int fin) { for(int i=0; i<fin; i++) { n = n / 10; } return n % 10; //return n/(int)(pow((double)10, fin)) % 10; } //用二維數組調用 static void Radix(int arr[], int len, int fin) //二維數組 fin判斷的依據,到底是 以什么位排序//時間復雜度O(n) 空間復雜度O(n) { int bucket[10][20] = {0};//桶 int num[10] = {0};//對應的桶中有多少個有效值 //所有的數據都以fin位為依據,放到了桶內 for(int i=0; i<len; i++)//數組的下標 從0開始放 { int index = Get_num(arr[i], fin);//獲取arr[i]的fin位的值,找到對應的桶 bucket[index][num[index]] = arr[i];//放到對用的桶中第num[index]位上 num[index]++;//對應的桶中有效個數++ } //從0-9桶內依次取值到arr里 int k = 0; for(int i=0; i<10; i++)//幾號桶 { for(int j=0; j<num[i]; j++)//j桶中有效值個數 { arr[k++] = bucket[i][j]; } } } //用鏈式隊列調用 static void Radix_queue(int arr[], int len, int fin) //時間復雜度O(n) 空間復 雜度O(n) { LQueue queArr[10]; for(int i=0; i<10; i++) { InitLQueue(&queArr[i]); } for(int i=0; i<len; i++) { int index = Get_num(arr[i], fin); Push(&queArr[index], arr[i]); } int k = 0; for(int i=0; i<10; i++) { while(!IsEmpty(&queArr[i])) { Pop(&queArr[i], &arr[k++]); } } for(int i=0; i<10; i++) { Destroy(&queArr[i]); } } void RadixSort(int arr[], int len)//時間復雜度O(dn) 空間復雜度(n)穩定 { //assert int count = Get_Figure(arr, len); for(int i=0; i<count; i++)//循環的趟數,低位優先 { Radix_queue(arr, len, i); } }