王道408数据结构——第八章 排序
文章目錄
- 一、排序定義
- 二、插入排序——直接插入排序
- 1. 描述
- 2. 代碼和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 6. 適用性
- 三、插入排序——折半插入排序
- 1. 描述
- 2. 時間效率
- 3. 穩定性
- 四、插入排序——希爾排序(縮小增量排序)
- 1. 描述
- 2. 代碼和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 6. 適用性
- 五、交換排序——冒泡排序
- 1. 描述
- 2. 代碼和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 六、交換排序——快速排序
- 1. 描述
- 2. 代碼和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 七、選擇排序——簡單選擇排序
- 1. 描述
- 2. 代碼
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 八、選擇排序——堆排序
- 1. 堆的定義
- 2. 建堆(構造初始堆)
- 3. 刪除結點
- 4. 插入結點
- 5. 算法與示例
- 6. 空間效率
- 7. 時間效率
- 8. 穩定性
- 9. 適用性
- 九、歸并排序
- 1. 描述
- 2. 代碼和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 十、基數排序
- 1. 描述
- 2. 算法和示例
- 3. 空間效率
- 4. 時間效率
- 5. 穩定性
- 十一、排序算法的比較
一、排序定義
重新排列表中的元素,使表中的元素滿足按關鍵字有序。
算法的穩定性:帶排序表中關鍵字相同的元素,其相對次序在排序前后不變,這稱這個排序算法是穩定的。算法是否穩定并不能衡量一個算法的優劣。如果帶排序表中的關鍵字均不重復,則排序結果是唯一的,算法的穩定性就無關緊要。
大部分的內部排序都需要執行比較和移動操作。通過比較兩個關鍵字的大小,確定對應元素的前后關系;然后通過移動元素以達到有序。
在基于比較的排序方法中,每次比較兩個關鍵字大小之后,僅出現兩種可能的轉移。因此可以用一棵二叉樹來描述比較判定過程,由此可以證明:當文件的 n 個關鍵字隨機分布時,任何借助比較的排序算法,至少需要O(nlog?2n)O(n\log_2n)O(nlog2?n)的時間。
二、插入排序——直接插入排序
1. 描述
插入排序是一種簡單直觀的排序方法,其基本思想是將一個待排序的記錄按其關鍵字大小插入前面已排好序的子序列。
插入排序通常采用就地排序,在從后向前的比較過程中,需要反復把已排序元素逐步向后挪位,為新元素騰出插入空間。
2. 代碼和示例
下面是直接插入排序的代碼
void insertSort(ElemType A[], int n){int i, j;for(i = 2; i <= n; i++){ // 依次對A[2]到A[n]進行插入排序if (A[i] < A[i-1]){ // 如果當前元素小于其前驅,將其插入前面序列A[0] = A[1]; // 哨兵,A[0]不存放元素for(j = i-1; A[0] < A[j]; j--) // 從后往前查找待插入位置A[j+1] = A[j]; // 元素挨個往后挪位A[j+1] = A[0]; // 最后一次循環,j指向待插入點的前一個位置。}} }
這里哨兵的作用是防止數組下標越界,提高查找效率
3. 空間效率
就地排序,僅使用了常數個輔助單元,空間復雜度為O(1)O(1)O(1)
4. 時間效率
排序過程中,向有序子表中逐個插入元素的操作進行了n-1趟;每趟操作都分為比較關鍵字和移動元素,次數取決于待排序表的初始狀態。
在最好情況下,表中元素已經有序,此時每插入一個元素,都只需一次比較而不需要移動元素。時間復雜度為O(n)O(n)O(n);
在最壞情況下。表中元素的順序剛好與排序結果相反(逆序),總的比較次數達到最大,為∑i=2ni\sum^n_{i=2}i∑i=2n?i,總的移動次數也達到最大,為∑i=2n(i+1)\sum^n_{i=2}(i+1)∑i=2n?(i+1);
在平均情況下,考慮待排序表中的元素是隨機的,此時取最好與最壞情況的平均值作為平均情況的時間復雜度。總的比較次數和總的移動次數均為n24\frac{n^2}44n2?。
因此,直接插入排序的時間復雜度為O(n2)O(n^2)O(n2)。
5. 穩定性
每次插入元素總是從后往前先比較在移動,算法是穩定的。
6. 適用性
直接插入排序算法時使用與順序儲存(大部分排序算法僅適用于順序儲存的線性表)和鏈式儲存。
其更適用于基本有序、數據量不大的排序表。
三、插入排序——折半插入排序
1. 描述
對于順序表,對插入位置的查找過程可以采用折半查找來實現。確定待插入位置后,再統一向后移動元素。
2. 時間效率
折半插入排序減少了比較元素的次數,總比較次數約為O(nlog?2n)O(n\log_2n)O(nlog2?n),該比較次數與表的初始狀態無關,僅取決于表中元素的個數;
而元素的移動次數并未改變,仍依賴于表的初始狀態。
因此,時間復雜度仍為O(n2)O(n^2)O(n2)。
但對于數據量不是很大的排序表,折半插入排序往往表現出很好的性能。
3. 穩定性
折半插入排序是一種穩定的排序方法。
四、插入排序——希爾排序(縮小增量排序)
1. 描述
基本思想:先將待排序表分割成若干形如[i,i+d,i+2d,?,i+kd][i,i+d,i+2d,\cdots,i+kd][i,i+d,i+2d,?,i+kd]的特殊子表,即把相隔某個增量的記錄組成一個子表,對各個子表進行直接插入排序;當整個表中的元素已呈“基本有序”時,再對全體記錄進行一次直接插入排序。
希爾排序的過程如下:
通常步長did_idi?的取值為d1=n2,di+1=?di2?,d最后一個=1d_1=\frac n2,d_{i+1}=\lfloor \frac {d_i}2\rfloor,d_{最后一個}=1d1?=2n?,di+1?=?2di???,d最后一個?=1
2. 代碼和示例
希爾排序的代碼如下
void shellSort(Elemtpye A[], int n){int dk, i, j;for(dk = n/2; dk >= 1; dk = dk/2){// 將各個子表的直接插入排序分解,合并為一個循環,一次循環過程僅按順序處理一個元素// 從第1個子表的第二個元素開始,第二次循環進行第2個子表的第二個元素for(i = dk+1; i < n; ++i){if(A[i] >= A[i-dk]) // 僅在當前元素無序時進行排序continue;A[0] = A[i]; // A[0]不是哨兵,僅暫存元素// j小于等于0時,說明該元素在該子表的已排序序列中最小for(j = i-dk; j >= 0 && A[0] < A[j]; j -= dk) A[j+dk] = A[j];A[j+dk] = A[0];}} }3. 空間效率
僅使用了常數個輔助單元,空間復雜度為O(1)O(1)O(1)
4. 時間效率
當n在某個特定范圍時,希爾排序的時間復雜度約為O(n1.3)O(n^{1.3})O(n1.3)。
在最壞情況下,希爾排序的時間復雜度為O(n2)O(n^2)O(n2)。
5. 穩定性
當相同關鍵字的記錄被劃分到不同子表時,可能會改變它們之間的相對次序,因此希爾排序是一種不穩定的排序方法。
6. 適用性
希爾排序對較大規模的排序都可以達到很高的效率。
僅適用于順序存儲的線性表。
五、交換排序——冒泡排序
1. 描述
所謂交換,是指根據序列中兩個元素關鍵字的比較結果來對換這兩個記錄在序列中的位置。
基本思想:從后往前(或從前往后)兩兩比較相鄰元素的值,若為逆序,則交換它們,直到序列比較完,這稱為一趟冒泡。一趟冒泡的結果是將最小(或最大)元素交換到待排序的第一個位置。下一趟冒泡時,已確定位置的元素不再參與比較。這樣最多做n-1趟冒泡就可以把所有元素排好。
冒泡排序中所產生的有序子序列一定是全局有序的,每趟排序都會將一個元素放在其最終位置。
2. 代碼和示例
冒泡排序算法代碼如下
void bubbleSort(ElemType A[], int n){for(i = 0; i < n-1; i ++){flag = false; // 標志位,記錄本趟冒泡是否發生交換for(int j = n-1; j > i; j--){ // 一趟冒泡,順序與外層循環相反if(A[j-1] > A[j]){ swap(A[j-1], A[j]);flag = true;}}if( !flag ) // 若某趟冒泡過程沒有發生交換,說明表已經有序return;} }3. 空間效率
僅使用了常數個輔助單元,空間復雜度為O(1)O(1)O(1)。
4. 時間效率
最好情況下,當初始序列有序時,第一趟冒泡標志位flag仍為false,直接跳出循環,比較次數為n-1,移動次數為0,時間復雜度為O(n)O(n)O(n);
最壞情況下,當初始序列逆序時,需要進行n-1趟排序,第 i 趟排序要進行 n-i 次關鍵字的比較,而且每次比較后都必須進行三次移動來交換元素的位置。比較次數=∑i=1n?1(n?i)=n(n?1)2,移動次數=∑i=1n?13(n?i)=3n(n?1)2比較次數=\sum^{n-1}_{i=1}(n-i)=\frac{n(n-1)}2,移動次數=\sum^{n-1}_{i=1}3(n-i)=\frac{3n(n-1)}2比較次數=i=1∑n?1?(n?i)=2n(n?1)?,移動次數=i=1∑n?1?3(n?i)=23n(n?1)?從而最壞情況下時間復雜度為O(n2)O(n^2)O(n2);
平均情況下,時間復雜度為O(n2)O(n^2)O(n2)。
5. 穩定性
冒泡排序時一種穩定的排序方法。
如果把代碼中判斷是否逆序的條件由“>”改為“≥”,則算法變得不穩定。
六、交換排序——快速排序
1. 描述
快速排序的基本思想是基于分治法的:在待排序表中選取一個元素,稱為樞軸(或稱基準,常取首元素)。通過一趟排序,將待排序表分成兩部分,一部分中所有元素均小于樞軸,另一部分元素均大于樞軸,兩部分分別位于樞軸元素的兩側,這個過程稱為一趟快速排序(或一次劃分)。然后遞歸地分別對兩個子表重復上述過程,直到每部分只有一個元素或空為止,此時所有元素都放在了最終位置。
快速排序并不產生有序子序列,但每趟排序后會將樞軸元素放在最終位置上。
2. 代碼和示例
一趟快速排序是一個交替搜索和交換的過程,算法如下
void quickSort(ElemType A[], int low, int high){if(low >= high) // 遞歸跳出條件。只有一個元素或為空return;// 一趟快排,將表劃分為兩個子表,返回樞軸位置int pivotpos = partition(A, low, high); quickSort(A, low, pivotpos-1); // 對左子表進行遞歸quickSort(A, pivotpos+1, high); // 對右子表進行遞歸 }// 一趟劃分,交替進行搜索交換 int partition(ElemType A[], int low, int high){ElemType pivot = A[low]; // 設為樞軸while(low < high){ // 從high往前搜索,找到最近的小于樞軸的元素,// 將其置入樞軸或者上一次交換留出空位中while(low < high && A[high] > pivot) high--;A[low] = A[high];// 從low往后搜索,找到最近的大于樞軸的元素,// 將其置入上一次交換留出的空位中。while(low < high && A[low] < pivot)low++;A[high] = A[low];}A[low] = pivot; // 將樞軸元素置入交替搜索后留出的空位中。return low; // 返回樞軸位置 }3. 空間效率
快排是遞歸地,需要借助一個遞歸工作棧來保持每層遞歸調用的必要信息,容量與遞歸調用的最大深度一致。
最好情況下,空間復雜度為O(log?2n)O(\log_2n)O(log2?n);
最壞情況下,因為要進行n-1次遞歸調用,棧的深度為O(n)O(n)O(n)
平均情況下,棧的深度為O(log?2n)O(\log_2n)O(log2?n)。
4. 時間效率
快速排序的運行時間和劃分是否對稱有關。
最好情況下,partition()可以做到最平衡的劃分,得到的兩個子問題大小都不大于n/2,這種情況下快速排序的運行速度將大大提升,此時時間復雜度為O(nlog?2n)O(n\log_2n)O(nlog2?n);
最壞情況下,劃分的兩個區域分別包含n-1個元素和0個元素。若初始表基本有序或基本逆序時,每層遞歸都出現最壞情況。此時時間復雜度為O(n2)O(n^2)O(n2);
平均情況下的快速排序與其最佳情況的運行時間很接近,時間復雜度為O(nlog?2n)O(n\log_2n)O(nlog2?n)。
有很多方法可以提升算法的效率:一種方法時盡可能選擇一個可以將數據平分的樞軸,如從序列的頭尾和中間選取三個元素,選擇三個元素的中間值作為樞軸;或隨機從表中選取一個樞軸,這樣做可以時最壞情況幾乎不會出現。
快速排序是所有內部排序中平均性能最優的排序算法。
5. 穩定性
某一趟中,兩個關鍵字相同的元素,從一個區間被交換到另一個區間的過程中,相對位置會發生變化。快速排序是一種不穩定的排序方法。
七、選擇排序——簡單選擇排序
1. 描述
基本思想:每一趟排序后,將剩余待排序元素中選擇關鍵字最小(或最大)的元素,放在其最終位置,最終位置的原有元素與其交換位置。
2. 代碼
簡單選擇排序代碼如下
void selectSort(ElemType A[], int n){for(int i = 0; i < n-1; i++){ // 到第n-1趟,待排元素只剩一個,就不用再選了int min = i;for(int j = i+1; j < n; j++){ // 選擇待排序列中的最小元素if(A[j] > A[i])min = j;}if(min != i)swap(A[i], A[min]);} }3. 空間效率
僅使用常數個輔助單元,空間效率為O(1)O(1)O(1)。
4. 時間效率
在簡單選擇排序中,元素移動的次數很少,不會超過3(n?1)3(n-1)3(n?1)次,最好情況是移動0次(此時對應表已有序)。但元素的比較次數和序列的初始狀態無關,始終是n(n?1)2\frac{n(n-1)}22n(n?1)?次,因此時間復雜度始終是O(n2)O(n^2)O(n2)
5. 穩定性
在第 i 趟把最小元素和第 i 個元素進行交換時,可能導致第 i 個元素與其后含有相同關鍵字元素的相對位置發生變化。簡單選擇排序是不穩定的。
八、選擇排序——堆排序
1. 堆的定義
若n個關鍵字序列滿足以下任一條件:
{L(i)?L(2i)∧L(i)?L(2i+1)(1?i??n2?)L(i)?L(2i)∧L(i)?L(2i+1)\left\{ \begin{array}{l} L(i)\geqslant L(2i)\wedge L(i)\geqslant L(2i+1)&(1\leqslant i\leqslant\lfloor\frac n2\rfloor)\\ L(i)\leqslant L(2i)\wedge L(i)\leqslant L(2i+1) \end{array} \right. {L(i)?L(2i)∧L(i)?L(2i+1)L(i)?L(2i)∧L(i)?L(2i+1)?(1?i??2n??)
該序列稱為堆。
堆可以視為一棵順序存儲的完全二叉樹,滿足第一個條件的稱為大根堆,其最大元素放在根節點,且堆的非葉結點的值均大于其左右子樹。,滿足第二個條件的稱為小根堆。
2. 建堆(構造初始堆)
n個結點的完全二叉樹,其最后一個非葉結點序號是?n2?\lfloor \frac n2\rfloor?2n??。從最后一個非葉結點開始往前一次遍歷結點,對每一個以當前結點為根的子樹進行檢查:對于大根堆,若根結點關鍵字小于左右孩子,將左右孩子中較大者與之交換,小根堆反之;交換后可能破壞下一級的堆,因此對下一級的堆重復進行檢查和交換,直到以當前結點為根的子樹構成堆為止。
下面是建立大根堆的算法
void buildMaxHeap(ElemType A[], int len){for(int i = len/2; i > 0; i--) // 從最小非葉結點開始,反復調整堆headAdjust(A, i, len); }void headAdjust(ElemType A[], int k, int len){A[0] = A[k]; // 暫存子樹根節點for(int i = 2*k; i <= len; i *= 2){ // 沿key值較大的結點往下if(i < len && A[i+1] > A[i]) // 左右子樹在順序表中總是相鄰i++; // 選擇key值較大的結點if(A[0] >= A[i]) // 當前堆已滿足性質break;A[k] = A[i]; // 調整結點k = i; // 進入較大葉結點的子樹}A[k] = A[0]; }調整的時間與數高h有關,為O(h)O(h)O(h)。
在建含 n 個元素的堆時,關鍵字的比較總次數不超過4n,時間復雜度為O(n)O(n)O(n),這說明可以在線性時間將一個無序數組建成一個堆。
3. 刪除結點
堆的刪除通常在根節點處,此時需要重新調整結構以保持性質。
輸出堆頂元素后,將堆底元素移到堆頂。再從堆頂元素開始向下調整,使其保持大堆頂的性質。
4. 插入結點
對堆進行插入時,先將新結點放在堆的末端,再對這新結點向上執行調整操作。
5. 算法與示例
下面給出堆排序算法,即依次刪除根節點的算法
void heapSort(ElemType A[], int len){buildMaxHeap(A, len); // 建立初始堆// 進行n-1趟交換和建堆過程。當i=1時,僅剩根節點,此時數組已經有序for(int i = len; i > 1; i--){ // 輸出堆頂元素(和堆底元素進行交換),此時數組中i~len的元素已經是全局有序的了swap(A[i], A[1]); headAdjust(A, 1, i-1); // 把剩余i-1個元素元素整理成堆} }建立初始堆的示例如下
輸出根節點87,將最后一個葉節點09置于根的位置,將剩余元素調整成新堆調整
在堆的末端插入新結點,重新調整堆
6. 空間效率
僅使用了常數個輔助單元,空間復雜度為O(1)O(1)O(1)
7. 時間效率
建堆時間為O(n)O(n)O(n),之后有n-1次向下調整操作,每次調整時間復雜度為O(h)。故在最好、最壞和平均情況下,堆排序的時間復雜度O(nlog?2n)O(n\log_2n)O(nlog2?n)。
8. 穩定性
在進行篩選時,有可能把后面相同關鍵字的元素調整到前面,所以堆排序時一種不穩定的排序方法。
9. 適用性
堆排序適合關鍵字較多的情況。
九、歸并排序
1. 描述
假定待排序表含有n 個記錄,則可將其視為n個有序的子表,每個子表長度為1。然后兩兩歸并(稱為2路歸并排序),再將得到的長度為2或1的有序表兩兩歸并,如此重復,直到合并成一個長度為n的有序表為止。
2. 代碼和示例
遞歸形式的歸并排序算法時基于分治的,分為分解和合并兩個步驟
void mergeSort(ElemType A[], int low, int high){if(low < heigh){ // 遞歸退出條件,當low==heigh時,子表長度為1,停止分解int mid = (low + high) / 2; // 分解:劃分兩個子表,對兩個子表遞歸地進行排序mergeSort(A, low, mid); mergeSort(A, mid+1, high);merge(A, low, mid, high); // 歸并:合并兩個已經排序的子表得到新的排序結構} }// 設立一個輔助數組 ElemType *B = (ElemType *)malloc( (n+1)*sizeof(ElemType) ); // 歸并兩個子表的過程與合并兩個有序鏈表的算法過程類似 void merge(ElemType A[], int low, int mid, int high){int i, j, k;for(i = low; i <= high; i++) // 將兩個子表中所有元素復制到B中的對應位置B[i] = A[i];for(i = low, j = mid+1, k = low; i <= mid && j <= high; k++){if(B[i] <= B[j])A[k] = B[i++];elseA[k] = B[j++];}while(i <= mid)A[k++] = B[i++];while(j <= high)A[k++] = B[j++];}3. 空間效率
merge()操作中,輔助空間剛好為n個單元,空間復雜度為O(n)O(n)O(n)。
4. 時間效率
每趟歸并的時間復雜度為O(n)O(n)O(n),需要進行?log?2n?\lceil\log_2n\rceil?log2?n?趟歸并,所以算法的時間復雜度為O(nlog?2n)O(n\log_2n)O(nlog2?n)。
一般而言,對于N個元素進行 k 路歸并排序時,排序的趟數 m 滿足km=Nk^m=Nkm=N,從而m=?log?kN?m=\lceil\log_kN\rceilm=?logk?N?。
從單個記錄起進行兩兩歸并并不值得提倡,通常將它和直接插入排序結合。改進后的歸并排序仍是穩定的。
5. 穩定性
merge()操作并不會改變相同關鍵字記錄的相對次序,算法是穩定的。
十、基數排序
1. 描述
基數排序十一種很特別的排序方法,它基于比較和移動進行排序,而基于關鍵字各位的大小進行排序。技術排序是一種借助多關鍵字排序的思想以對單邏輯關鍵字進行排序的方法。
假設長度為 n 的線性表中每個結點aja_jaj?的關鍵字由 d 元組(kjd?1,kjd?2,?,kj1,kj0)(k_j^{d-1},k_j^{d-2},\cdots,k_j^1,k_j^0)(kjd?1?,kjd?2?,?,kj1?,kj0?)組成,滿足0≤kji≤r?10\leq k_j^i\leq r-10≤kji?≤r?1。其中kjd?1k_j^{d-1}kjd?1?稱為最主位關鍵字,kj0k_j^0kj0?稱為最次位關鍵字。
為實現多關鍵字排序,通常由兩種方法:
- 最高位優先(MSD):按關鍵字位權重遞減依次逐層劃分成若干更小的子序列,最后將所有子序列依次連接成一個有序序列。
- 最低位優先(LSD):按關鍵字位權重遞增依次進行排序,最后形成一個有序子序列。
2. 算法和示例
下面描述以 r 為基數的最低位優先基數排序的過程
在排序過程中,使用 r 個隊列Q0,Q1,?,Qr?1Q_0,Q_1,\cdots,Q_{r-1}Q0?,Q1?,?,Qr?1?。基數排序的過程如下:
從i=0開始(數字最低位),依次做一次“分配”和“收集”。
- 分配:開始時,把Q0,Q1,?,Qr?1Q_0,Q_1,\cdots,Q_{r-1}Q0?,Q1?,?,Qr?1?各個隊列置空,然后依次考察線性表中的每個結點aj(j=0,1,?,n?1)a_j(j=0,1,\cdots,n-1)aj?(j=0,1,?,n?1),若aja_jaj?關鍵字kji=kk_j^i=kkji?=k,就把aja_jaj?放進QkQ_kQk?隊列中。
- 收集:把Q0,Q1,?,Qr?1Q_0,Q_1,\cdots,Q_{r-1}Q0?,Q1?,?,Qr?1?各個隊列首位相接,得到新的結點序列,從而組成新的線性表。
通常采用鏈式基數排序,假設對如下10個記錄進行排序:
每個關鍵字是1000以下的正整數,由3位子關鍵字K1K2K3K^1K^2K^3K1K2K3構成,分別代表百位、十位、個位,一共需要進行三趟分配-收集操作。基數r=10,在排序過程中需要借助10個鏈隊列。
第一趟分配用最低位關鍵字(個位)K3K^3K3進行。將所有K3K^3K3相等的記錄分配到同一個隊列,然后進行收集。
第二趟分配用次低位關鍵字(十位)K2K^2K2進行,將所有K2K^2K2相等的記錄分配到同一個隊列,然后進行收集。
第三趟分配用最高位關鍵字(百位)K1K^1K1進行,將所有K3K^3K3相等的記錄分配到同一個隊列,然后進行收集。自此整個排序結束
3. 空間效率
一趟排序需要輔助空間為 r(r個隊列,r個隊頭指針和隊尾指針)。空間復雜度為O(r)O(r)O(r)。
4. 時間效率
基數排序需要進行 d 趟分配和收集,一趟分配需要O(n)O(n)O(n),一趟收集需要O(r)O(r)O(r)。所以基數排序的時間復雜度為O(d(n+r))O(d(n+r))O(d(n+r)),其與序列的初始狀態無關。
5. 穩定性
基數排序是穩定的。
十一、排序算法的比較
| 算法種類 | 時間復雜度 | 空間復雜度 | 是否穩定 | ||
| 最好情況 | 最壞情況 | 平均情況 | |||
| 直接插入排序 (插入排序) | O(n) | O(n2) | O(n2) | O(1) | 穩定 |
| 冒泡排序 (交換排序) | O(n) | O(n2) | O(n2) | O(1) | 穩定 |
| 簡單選擇排序 (選擇排序) | O(n2) | O(n2) | O(n2) | O(1) | 不穩定 |
| 希爾排序 (插入排序) | 依賴增量函數 | O(1) | 不穩定 | ||
| 快速排序 (交換排序) | O(n2) | 最壞情況下是O(n) | 不穩定 | ||
| 堆排序 (選擇排序) | O(1) | 不穩定 | |||
| 2路歸并排序 | O(n) | 穩定 | |||
| 基數排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(r) | 穩定 |
- 在實際應用中,快速排序往往可以優于其他算法,被認為是目前基于比較的內部排序中最好的方法。
- 冒泡排序和堆排序每趟處理后都能產生當前的最大值或最小值。
- 快速排序一趟處理就能確定一個元素的最終位置。
- 若 n 較小,可以采用直接插入排序或簡單選擇排序;
- 由于直接插入排序所需的記錄移動次數較簡單選擇排序多,當記錄本身信息量較大時,選用簡單選擇排序較好。
- 若文件的初始狀態已經按關鍵字基本有序,選用直接插入排序或冒泡排序。
- 若 n 較大,則應采用時間復雜度為Olog?2n)O\log2n)Olog2n)的排序方法:快速排序、推排序或歸并排序;
- 當關鍵字隨機分布時,快速排序平均時間最短;
- 堆排序所需的輔助空間少于快速排序,且不會出現快速排序可能出現的最壞情況;
- 若要求排序穩定,則可選用歸并排序。
- 若 n 很大、記錄的關鍵字位數較少且可以分解時,采用基數排序較好
- 當記錄本身信息量較大時,為避免耗費大量時間移動記錄,可以采用鏈表作為存儲結構。
總結
以上是生活随笔為你收集整理的王道408数据结构——第八章 排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 王道408数据结构——第七章 查找
- 下一篇: COMA(一): Learning to