排序算法时间复杂度、空间复杂度、稳定性比较
排序算法分類
排序算法比較表格填空
| 冒泡排序 | :————-: | :—–: | :—–: | :—–: |
| 選擇排序 | :————-: | :—–: | :—–: | :—–: |
| 直接插入排序 | :————-: | :—–: | :—–: | :—–: |
| 歸并排序 | :————-: | :—–: | :—–: | :—–: |
| 快速排序 | :————-: | :—–: | :—–: | :—–: |
| 堆排序 | :————-: | :—–: | :—–: | :—–: |
| 希爾排序 | :————-: | :—–: | :—–: | :—–: |
| 計數排序 | :————-: | :—–: | :—–: | :—–: |
| 基數排序 | :————-: | :—–: | :—–: | :—–: |
排序算法比較表格
| 冒泡排序 | O(n2)O(n2) | O(n2)O(n2) | O(1)O(1) | 是 |
| 選擇排序 | O(n2)O(n2) | O(n2)O(n2) | O(1)O(1) | 不是 |
| 直接插入排序 | O(n2)O(n2) | O(n2)O(n2) | O(1)O(1) | 是 |
| 歸并排序 | O(nlogn)O(nlogn) | O(nlogn)O(nlogn) | O(n)O(n) | 是 |
| 快速排序 | O(nlogn)O(nlogn) | O(n2)O(n2) | O(logn)O(logn) | 不是 |
| 堆排序 | O(nlogn)O(nlogn) | O(nlogn)O(nlogn) | O(1)O(1) | 不是 |
| 希爾排序 | O(nlogn)O(nlogn) | O(ns)O(ns) | O(1)O(1) | 不是 |
| 計數排序 | O(n+k)O(n+k) | O(n+k)O(n+k) | O(n+k)O(n+k) | 是 |
| 基數排序 | O(N?M)O(N?M) | O(N?M)O(N?M) | O(M)O(M) | 是 |
注:
1 歸并排序可以通過手搖算法將空間復雜度降到O(1),但是時間復雜度會提高。
2 基數排序時間復雜度為O(N*M),其中N為數據個數,M為數據位數。
輔助記憶
- 時間復雜度記憶-?
- 冒泡、選擇、直接 排序需要兩個for循環,每次只關注一個元素,平均時間復雜度為O(n2)O(n2)(一遍找元素O(n)O(n),一遍找位置O(n)O(n))
- 快速、歸并、希爾、堆基于二分思想,log以2為底,平均時間復雜度為O(nlogn)O(nlogn)(一遍找元素O(n)O(n),一遍找位置O(logn)O(logn))
- 穩定性記憶-“快希選堆”(快犧牲穩定性)?
- 排序算法的穩定性:排序前后相同元素的相對位置不變,則稱排序算法是穩定的;否則排序算法是不穩定的。
原理理解
1 冒泡排序
1.1 過程
冒泡排序從小到大排序:一開始交換的區間為0~N-1,將第1個數和第2個數進行比較,前面大于后面,交換兩個數,否則不交換。再比較第2個數和第三個數,前面大于后面,交換兩個數否則不交換。依次進行,最大的數會放在數組最后的位置。然后將范圍變為0~N-2,數組第二大的數會放在數組倒數第二的位置。依次進行整個交換過程,最后范圍只剩一個數時數組即為有序。
1.2 動圖
1.3 核心代碼(函數)
//array[]為待排序數組,n為數組長度 void BubbleSort(int array[], int n) {int i, j, k;for(i=0; i<n-1; i++)for(j=0; j<n-1-i; j++){if(array[j]>array[j+1]){k=array[j];array[j]=array[j+1];array[j+1]=k;}} }2 選擇排序
2.1 過程
選擇排序從小到大排序:一開始從0~n-1區間上選擇一個最小值,將其放在位置0上,然后在1~n-1范圍上選取最小值放在位置1上。重復過程直到剩下最后一個元素,數組即為有序。
2.2 動圖
?
2.3 核心代碼(函數)
//array[]為待排序數組,n為數組長度 void selectSort(int array[], int n) {int i, j ,min ,k;for( i=0; i<n-1; i++){min=i; //每趟排序最小值先等于第一個數,遍歷剩下的數for( j=i+1; j<n; j++) //從i下一個數開始檢查{if(array[min]>array[j]){min=j;}}if(min!=i){k=array[min];array[min]=array[i];array[i]=k;}} }3 插入排序
3.1 過程
插入排序從小到大排序:首先位置1上的數和位置0上的數進行比較,如果位置1上的數大于位置0上的數,將位置0上的數向后移一位,將1插入到0位置,否則不處理。位置k上的數和之前的數依次進行比較,如果位置K上的數更大,將之前的數向后移位,最后將位置k上的數插入不滿足條件點,反之不處理。
3.2 動圖
?
3.3 核心代碼(函數)
//array[]為待排序數組,n為數組長度 void insertSort(int array[], int n) {int i,j,temp;for( i=1;i<n;i++){if(array[i]<array[i-1]){temp=array[i];for( j=i;array[j-1]>temp;j--){array[j]=array[j-1];}array[j]=temp;}} }4 歸并排序
4.1 過程
歸并排序從小到大排序:首先讓數組中的每一個數單獨成為長度為1的區間,然后兩兩一組有序合并,得到長度為2的有序區間,依次進行,直到合成整個區間。
4.2 動圖
?
4.3 核心代碼(函數)
- 遞歸實現
- 迭代實現
5 快速排序
5.1 過程
快速排序從小到大排序:在數組中隨機選一個數(默認數組首個元素),數組中小于等于此數的放在左邊,大于此數的放在右邊,再對數組兩邊遞歸調用快速排序,重復這個過程。
5.2 動圖
?
5.3 核心代碼(函數)
推薦程序(好理解)
//接口調整 void adjust_quicksort(int k[],int n) { quicksort(k,0,n-1); } void quicksort(int a[], int left, int right) { int i,j,t,temp; if(left>right) //(遞歸過程先寫結束條件)return; temp=a[left]; //temp中存的就是基準數 i=left; j=right; while(i!=j) { //順序很重要,要先從右邊開始找(最后交換基準時換過去的數要保證比基準小,因為基準 //選取數組第一個數,在小數堆中) while(a[j]>=temp && i<j) j--; //再找右邊的 while(a[i]<=temp && i<j) i++; //交換兩個數在數組中的位置 if(i<j) { t=a[i]; a[i]=a[j]; a[j]=t; } } //最終將基準數歸位 (之前已經temp=a[left]過了,交換只需要再進行兩步)a[left]=a[i]; a[i]=temp; quicksort(left,i-1);//繼續處理左邊的,這里是一個遞歸的過程 quicksort(i+1,right);//繼續處理右邊的 ,這里是一個遞歸的過程 }6 堆排序
6.1 過程
堆排序從小到大排序:首先將數組元素建成大小為n的大頂堆,堆頂(數組第一個元素)是所有元素中的最大值,將堆頂元素和數組最后一個元素進行交換,再將除了最后一個數的n-1個元素建立成大頂堆,再將最大元素和數組倒數第二個元素進行交換,重復直至堆大小減為1。
-
注:完全二叉樹?
假設二叉樹深度為n,除了第n層外,n-1層節點都有兩個孩子,第n層節點連續從左到右。如下圖? -
注:大頂堆?
大頂堆是具有以下性質的完全二叉樹:每個節點的值都大于或等于其左右孩子節點的值。?
即,根節點是堆中最大的值,按照層序遍歷給節點從1開始編號,則節點之間滿足如下關系:?
?(1<=i<=n/2)
6.2 動圖
?
?
6.3 核心代碼(函數)
注意!!!數組從1開始,1~n
7 希爾排序
7.1 過程
希爾排序是插入排序改良的算法,希爾排序步長從大到小調整,第一次循環后面元素逐個和前面元素按間隔步長進行比較并交換,直至步長為1,步長選擇是關鍵。
7.2 動圖
?
7.3 核心程序(函數)
//下面是插入排序 void InsertSort( int array[], int n) {int i,j,temp;for( i=0;i<n;i++ ){if(array[i]<array[i-1]){temp=array[i];for( j=i-1;array[j]>temp;j--){array[j+1]=array[j];}array[j+1]=temp;}} } //在插入排序基礎上修改得到希爾排序 void SheelSort( int array[], int n) {int i,j,temp;int gap=n; //~~~~~~~~~~~~~~~~~~~~~do{gap=gap/3+1; //~~~~~~~~~~~~~~~~~~for( i=gap;i<n;i++ ){if(array[i]<array[i-gap]){temp=array[i];for( j=i-gap;array[j]>temp;j-=gap){array[j+gap]=array[j];}array[j+gap]=temp;}}}while(gap>1); //~~~~~~~~~~~~~~~~~~~~~~}8 桶排序(基數排序和基數排序的思想)
8.1 過程
桶排序是計數排序的變種,把計數排序中相鄰的m個”小桶”放到一個”大桶”中,在分完桶后,對每個桶進行排序(一般用快排),然后合并成最后的結果。
8.2 圖解
8.3 核心程序
#include <stdio.h> int main() {int a[11],i,j,t;for(i=0;i<=10;i++)a[i]=0; //初始化為0for(i=1;i<=5;i++) //循環讀入5個數{scanf("%d",&t); //把每一個數讀到變量t中a[t]++; //進行計數(核心行)}for(i=0;i<=10;i++) //依次判斷a[0]~a[10]for(j=1;j<=a[i];j++) //出現了幾次就打印幾次printf("%d ",i);getchar();getchar(); //這里的getchar();用來暫停程序,以便查看程序輸出的內容//也可以用system("pause");等來代替return 0; }9 計數排序
9.1 過程
算法的步驟如下:?
- 找出待排序的數組中最大和最小的元素?
- 統計數組中每個值為i的元素出現的次數,存入數組C的第i項?
- 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)?
- 反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1
9.2 圖解
9.3 核心程序(函數)
程序1: #define NUM_RANGE (100) //預定義數據范圍上限,即K的值void counting_sort(int *ini_arr, int *sorted_arr, int n) //所需空間為 2*n+k { int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE); int i, j, k; //初始化統計數組元素為值為零 for(k=0; k<NUM_RANGE; k++){ count_arr[k] = 0; } //統計數組中,每個元素出現的次數 for(i=0; i<n; i++){ count_arr[ini_arr[i]]++; } //統計數組計數,每項存前N項和,這實質為排序過程for(k=1; k<NUM_RANGE; k++){ count_arr[k] += count_arr[k-1]; } //將計數排序結果轉化為數組元素的真實排序結果for(j=n-1 ; j>=0; j--){ int elem = ini_arr[j]; //取待排序元素int index = count_arr[elem]-1; //待排序元素在有序數組中的序號sorted_arr[index] = elem; //將待排序元素存入結果數組中count_arr[elem]--; //修正排序結果,其實是針對算得元素的修正} free(count_arr); } 程序2:C++(最大最小壓縮桶數) public static void countSort(int[] arr) {if (arr == null || arr.length < 2) {return;}int min = arr[0];int max = arr[0];for (int i = 1; i < arr.length; i++) {min = Math.min(arr[i], min);max = Math.max(arr[i], max);}int[] countArr = new int[max - min + 1];for (int i = 0; i < arr.length; i++) {countArr[arr[i] - min]++;}int index = 0;for (int i = 0; i < countArr.length; i++) {while (countArr[i]-- > 0) {arr[index++] = i + min;} }10 基數排序
10.1 過程
基數排序是基于數據位數的一種排序算法。?
它有兩種算法?
①LSD–Least Significant Digit first 從低位(個位)向高位排。?
②MSD– Most Significant Digit first 從高位向低位(個位)排。?
時間復雜度O(N*最大位數)。?
空間復雜度O(N)。
10.2 圖解
?
對a[n]按照個位0~9進行桶排序:?
?
對b[n]進行累加得到c[n],用于b[n]中重復元素計數?
!!!b[n]中的元素為temp中的位置!!!跳躍的用++補上:?
?
temp數組為排序后的數組,寫回a[n]。temp為按順序倒出桶中的數據(聯合b[n],c[n],a[n]得到),重復元素按順序輸出:?
10.3 核心程序
//基數排序 //LSD 先以低位排,再以高位排 //MSD 先以高位排,再以低位排 void LSDSort(int *a, int n) { assert(a); //判斷a是否為空,也可以a為空||n<2返回int digit = 0; //最大位數初始化for (int i = 0; i < n; ++i) { //求最大位數while (a[i] > (pow(10,digit))) //pow函數要包含頭文件math.h,pow(10,digit)=10^digit{ digit++; } } int flag = 1; //位數for (int j = 1; j <= digit; ++j) { //建立數組統計每個位出現數據次數(Digit[n]為桶排序b[n]) int Digit[10] = { 0 }; for (int i = 0; i < n; ++i) { Digit[(a[i] / flag)%10]++; //flag=1時為按個位桶排序} //建立數組統計起始下標(BeginIndex[n]為個數累加c[n],用于記錄重復元素位置//flag=1時,下標代表個位數值,數值代表位置,跳躍代表重復)int BeginIndex[10] = { 0 }; for (int i = 1; i < 10; ++i) { //累加個數BeginIndex[i] = BeginIndex[i - 1] + Digit[i - 1]; } //建立輔助空間進行排序 //下面兩條可以用calloc函數實現int *tmp = new int[n]; memset(tmp, 0, sizeof(int)*n);//初始化 //聯合各數組求排序后的位置存在temp中for (int i = 0; i < n; ++i) { int index = (a[i] / flag)%10; //桶排序和位置數組中的下標//計算temp相應位置對應a[i]中的元素,++為BeginIndex數組數值加1//跳躍間隔用++來補,先用再++tmp[BeginIndex[index]++] = a[i]; } //將數據重新寫回原空間 for (int i = 0; i < n; ++i) { a[i] = tmp[i]; } flag = flag * 10; delete[] tmp; } }附:
1 完整程序框架(冒泡排序舉例)
1.1 VS2010程序
#include "stdafx.h" #include "stdio.h" #include <stdlib.h>void BubbleSort(int array[], int n){int i,j,k,count1=0, count2=0;for(i=0; i<n-1; i++)for(j=n-1; j>i; j--){count1++;if(array[j-1]>array[j]){count2++;k=array[j-1];array[j-1]=array[j];array[j]=k;}}printf("總共的循環次序為:%d, 總共的交換次序為:%d\n\n", count1, count2); }int main(int argc, _TCHAR* argv[]) {int as[]={0,1,2,3,4,6,8,5,9,7};BubbleSort(as, 10);for(int i=0; i<10; i++){printf("%d", as[i]);}printf("\n\n");system("pause");return 0; }1.2 執行程序(OJ)
#include <stdio.h>void BubbleSort(int array[], int n){int i,j,k,count1=0, count2=0;for(i=0; i<n-1; i++)for(j=n-1; j>i; j--){count1++;if(array[j-1]>array[j]){count2++;k=array[j-1];array[j-1]=array[j];array[j]=k;}}printf("總共的循環次序為:%d, 總共的交換次序為:%d\n\n", count1, count2); }int main() {int as[]={0,1,2,3,4,6,8,5,9,7};BubbleSort(as, 10);int i=0;for(i=0; i<10; i++){printf("%d", as[i]);}return 0; }2 關于交換的優化
不用中間變量進行交換
if(A[j] <= A[i]){A[j] = A[j] + A[i];A[i] = A[j] - A[i];A[j] = A[j] - A[i]; }3 C語言實現數組動態輸入
#include <stdio.h> #include <assert.h> //斷言頭文件 #include <stdlib.h> int main(int argc, char const *argv[]) { int size = 0; scanf("%d", &size); //首先輸入數組個數assert(size > 0); //判斷數組個數是否非法int *array = (int *)calloc(size, sizeof(int)); //動態分配數組if(!R1) { return; //申請空間失敗 } int i = 0; for (i = 0; i < size; ++i) { scanf("%d", &array[i]); } mergeSort(array, size); printArray(array, size); free(array); return 0; }注:?
1.colloc與malloc類似,但是主要的區別是存儲在已分配的內存空間中的值默認為0,使用malloc時,已分配的內存中可以是任意的值.?
2.colloc需要兩個參數,第一個是需要分配內存的變量的個數,第二個是每個變量的大小.
歡迎關注微信公眾號:嵌入式Linux??
?
總結
以上是生活随笔為你收集整理的排序算法时间复杂度、空间复杂度、稳定性比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js上传文件并预览文件内容
- 下一篇: 《从三月开始……》