排序(2)--選擇排序,歸併排序和基數排序
一.選擇排序:
1.簡單選擇排序:
(1).思想: 首先通過 n –1 次關鍵字比較,從 n 個記錄中找出關鍵字最小的記錄,將它與第一個記錄交換。
再通過 n –2 次比較,從剩餘的 n –1 個記錄中找出關鍵字次小的記錄,將它與第二個記錄交換。
重複上述操作,共進行 n –1 趟排序後,排序結束。
(2)實現:
void SelectSort (SqList &L) { // 對順序表 L 作簡單選擇排序
for (i = 1; i < L.length; ++ i) {
k = i;
for ( j = i+1; j <= n; j++) if (L.r[j].key < L.r[k].key) k = j;
if (i != k) L.r[i]←→L.r[k]; // 與第 i 個記錄交換
}
} // SelectSort
(3).演算法分析:
時間複雜度:O(n2);
空間複雜度:O(1)——交換時用到一個暫存單元
穩定性:由於存在著不相鄰元素之間的互換,因此,簡單選擇排序是“不穩定的” 。
2.樹形選擇排序(競標賽排序):
(1)思想: 首先對 n 個記錄的關鍵字進行兩兩比較,得到 n/2個優勝者(關鍵字小者),作為第一步比較的結果保留下來。然後在這 n/2 個較小者之間再進行兩兩比 較,…,如此重複,直到選出最小關鍵字的記錄為止
.......................
(2)演算法分析:
時間複雜度:O(nlog2n)-----------n個記錄各自比較約log2n次
空間複雜度:O(n) —勝者樹的附加內結點共有n-1個!
穩定性:穩定------左右結點相同者左為先
3.堆排序:
(1)概念:設有n個元素的序列k1,k2,…,kn,當且僅當滿足下述關係之一時,稱之為堆
*樹中所有結點的值均大於(或小於)其左右孩子,此樹的根結點(即堆頂)必最大(或最小)
(2)建堆(建小堆為例):
假若完全二叉樹的某一個結點i,它的左、右子樹已是堆。需要將R[2i].key與R[2i+1].key之中的最小者與R[i].key比較,若R[i].key較大則交換,這有可能破壞下 一級堆。於是繼續採用上述方法構造下一級堆,直到完全二叉樹中結點i構成堆為止。
void HeapAdjust(SqList &H, int s, int m)
{//已知H.r[s..m]中記錄的關鍵字除H.r[s].key之外均滿足堆的定義,
//本函式調整H.r[s]的關鍵字,使H.r[s..m]成為一個大頂堆
rc = H.r[s];
for (j=2*s; j<=m; j*=2)
{
//沿key較大的孩子結點向下篩選,j為key較大的記錄的下標
if(j<m && LT(H.r[j].key, H.r[j+1].key)) ++j;
if(!LT(rc.key,H.r[j].key)) break;
H.r[s]=H.r[j];
s=j; //rc應插入在位置s上
}
H.r[s] = rc; //插入
} //HeapAdjust
(3)怎樣進行堆排序
關鍵:將堆的當前頂點輸出後,如何將剩餘序列重新調整為堆?
方法:將當前頂點與堆尾記錄交換,然後仿建堆動作重新調整,如此反覆直至排序結束。
對建好的大堆進行排序:
.....知道最後記錄是從小到大的。
void HeapSort (HeapType &H)
{ //對順序表H進行堆排序。
for(i=H.length/2;i>0;--i) //把H.r[1..length]建成大頂堆
HeapAdjust(H,i,H.length);
for(i=H.length; i>1; --i)
{ //將堆頂記錄和當前未經排
//序子序列H.r.[1..i]中最後一個記錄相互交換
H.r[1]<---> H.r[i];
HeapAdjust(H,1,i-1); //將H.R[1..i-1]重新調整為大頂堆
}
} //HeapSort
(4)演算法分析;
時間效率: O(nlog2n)。因為整個排序過程中需要呼叫n-1次HeapAdjust( )演算法, HeapAdjust( )而演算法本身耗時為log2n;
空間效率:O(1)。僅在第二個for迴圈中交換記錄時用到一個臨時變數temp。
穩定性: 不穩定。
優點:對少量資料效果不明顯,但對大量資料有效
二.歸併排序:
1.思想:將兩個(或以上)的有序表組成新的有序表。
可以把一個長度為n的無序序列看成是 n 個長度為 1 的有序子序列 ,首先做兩兩歸併,得到 n / 2 個長度為 2 的子序列 ;再做兩兩歸併,…,如此重複,直到最後 得到一個長度為n 的有序序列。
2.實現:
一趟歸併排序演算法 (兩路有序併為一路)
void Merge (SR,&TR,i, m, n) {
// 將有序的SR[i…m]和SR[m+1…n]歸併為有序的TR[i…n]
for(k=i , j=m+1; i<=m && j<=n; ++k ) {
if ( SR[i]<= SR[j] )TR[k]=SR[i++];
else TR[k]=SR[j++]; // 將SR中記錄由小到大地併入TR
}
if (i<=m) TR[k…n]=SR[i…m]; // 將剩餘的SR[i…m]複製到TR
if (j<=n) TR[k…n]=SR[j…n]; // 將剩餘的SR[j…n]複製到TR
} // Merge
遞迴形式的兩路歸併排序演算法
void MSort (SR,&TR1,s, t) {
// 將無序的SR[s…t]歸併排序為TR1[s…t]
if ( s==t )TR1[s]=SR[s]; // 當len=1時返回
else {
m=(s+t)/2; // 將SR [s…t]平分為SR [s…m]和SR [m+1…t]
MSort (SR,&TR2,s, m); // 將SR 一分為二, 2分為4…
// 遞迴地將SR [s…m]歸併為有序的TR2[s…m]
MSort (SR,&TR2,m+1, t );
// 遞迴地將SR [m+1…t]歸併為有序的TR2[m+1…t]
Merge(TR2, TR1, s, m, t );
// 將TR2 [s…m]和TR2 [m+1…t]歸併到TR1 [s…t]
}
} // MSort
*初次呼叫時為(L, TR, 1, length)
3.演算法分析:
時間效率 :O(nlog2n) 一趟歸併排序的操作是:呼叫[n/2h]次演算法merge將SR[1..n]中前後相鄰且長度為h的有序段進行兩兩歸併,得到前後相鄰長度為2h的有序段, 並存放在TR[1..n]中,整個歸併排序需要進行[log2n]趟,所以演算法總的時間複雜度為O(nlog2n)。
空間效率 :O(n) 因為需要一個與原始序列同樣大小的輔助序列(TR)。這正是此演算法的缺點。
穩定性:穩定
三.基數排序:
1.兩種思路:
最高位優先法MSD (Most Significant Digit first)
最低位優先法LSD (Least Significant Digit first)
例:對一副撲克牌該如何排序?
答:若規定花色為第一關鍵字(高位),面值為第二關鍵字(低位),則使用MSD和LSD方法都可以達到排序目的。
MSD方法的思路:先設立4個花色“箱”,將全部牌按花色分別歸入4個箱內(每個箱中有13張牌);然後對每個箱中的牌按面值進行插入排序(或其它穩 定演算法)。
LSD方法的思路:先按面值分成13堆(每堆4張牌),然後對每堆中的牌按花色進行排序(用插入排序等穩定的演算法)。
2.計算機實現:
再看一例:
例:初始關鍵字序列T=(32, 13, 27, 32*, 19,33),請分別用MSD和LSD進行排序,並討論其優缺點。
法1(MSD):原始序列:32, 13, 27, 32*, 19, 33
先按高位Ki1 排序:(13, 19), 27, (32, 32*,33)
再按低位 Ki2排序 : 13, 19 , 27, 32, 32*, 33
因為有分組,故此演算法需遞迴實現
法2(LSD): 原始序列: 32, 13, 27, 32*, 19 ,33
先按低位Ki2排序: 32, 32*, 13, 33, 27, 19
再按高位Ki1排序: 13, 19 , 27, 32, 32*, 33
無需分組,易程式設計實現!
3.LSD實現(順序表);
排序時經過了反覆的“分配”和“收集”過程。當對關鍵字所有的位進行掃描排序後,整個序列便從無序變為有序了。
討論:所用佇列是順序結構,浪費空間,能否改用鏈式結構?
4.LSD實現(鏈式)
5.演算法分析;
假設有n 個記錄, 每個記錄的關鍵字有d 位,每個關鍵字的取值有radix個, 則需要radix個佇列, 進行d趟“分配”與“收集”。因此時間複雜度:O( d ( n+radix ) )。
空間複雜度:O(radix). 基數排序需要增加n+2radix個附加連結指標,空間效率低
穩定性:穩定。(一直前後有序)。
相關文章
- 歸併排序和基數排序排序
- Python八大演算法的實現,插入排序、希爾排序、氣泡排序、快速排序、直接選擇排序、堆排序、歸併排序、基數排序。Python演算法排序
- [排序] 歸併排序排序
- 七、排序,選擇、冒泡、希爾、歸併、快速排序實現排序
- 選擇排序和快速排序排序
- 快速排序&&歸併排序排序
- php插入排序,快速排序,歸併排序,堆排序PHP排序
- 選擇排序和氣泡排序排序
- 氣泡排序和選擇排序排序
- 選擇排序和插入排序排序
- 四、歸併排序 && 快速排序排序
- 計數排序、桶排序和基數排序排序
- 排序之選擇排序排序
- 排序演算法 - 氣泡排序和選擇排序排序演算法
- 排序演算法Python(冒泡、選擇、快速、插入、希爾、歸併排序)排序演算法Python
- 歸併排序--二路排序排序
- 排序演算法__歸併排序排序演算法
- 排序演算法:歸併排序排序演算法
- 歸併排序--排序演算法排序演算法
- 排序演算法 - 歸併排序排序演算法
- 排序演算法——歸併排序排序演算法
- 排序演算法(歸併排序)排序演算法
- O(lgn)的三種排序,快速排序、歸併排序、堆排序排序
- 桶排序和基數排序排序
- 歸併排序排序
- 氣泡排序、歸併排序與快速排序比較排序
- (建議收藏)2020最新排序演算法總結:冒泡、選擇、插入、希爾、快速、歸併、堆排序、基數排序排序演算法
- 氣泡排序和選擇排序詳解排序
- 氣泡排序和選擇排序流程圖排序流程圖
- 演算法導論學習之三:排序之C語言實現:選擇排序,插入排序,歸併排序演算法排序C語言
- 排序演算法(氣泡排序,選擇排序,插入排序,希爾排序)排序演算法
- 【JS面試向】選擇排序、桶排序、氣泡排序和快速排序簡介JS面試排序
- 排序演算法之 '歸併排序'排序演算法
- php實現 歸併排序,快速排序PHP排序
- (一)氣泡排序、選擇排序、插入排序排序
- 歸併排序求逆序數排序
- 利用java實現插入排序、歸併排序、快排和堆排序Java排序
- 排序演算法__選擇排序排序演算法