【排序】排序演算法之選擇排序

來源:互聯網
上載者:User

排序演算法之選擇排序   

羅朝輝 (http://www.cnblogs.com/kesalin/)本文遵循“署名-非商業用途-保持一致”創作公用協議


排序是資料處理中經常使用的一種重要運算,在電腦及其應用系統中,花費在排序上的時間在系統已耗用時間中佔有很大比重,其重要性無需多言。下文將介紹常用的如下排序方法,對它們進行簡單的分析和比較,並提供 C 語言實現。

 

所謂排序,就是要將一堆記錄,使之按關鍵字遞增(或遞減)次序排列起來。根據排序所採用的策略,可以分為如下五種:

1、插入排序(直接插入排序、希爾排序);

2、交換排序(冒泡排序、快速排序);

3、選擇排序(直接選擇排序、堆排序);

4、歸併排序;

5、桶排序(桶排序,基數排序);

 

---------------------------------------------------------------------------------

 

前面講了插入,交換排序,下面接著來講選擇排序。

選擇排序(Selection Sort)的基本思想是:每一趟從待排序的記錄中選出關鍵字最小的記錄,順序放在已排好序的子檔案的最後,直到全部記錄排序完畢。

常用的選擇排序方法有直接選擇排序和堆排序。

 

直接選擇排序

基本思想:將待排序記錄分成有序區和無序區,初始狀態下有序區位空,無序區為整個待排序記錄。每一趟選擇排序就是在無序區中選擇最小(或最大)的記錄,插入到有序區的最後。如此迴圈直到無序區為空白,完成排序。

 

代碼實現

// 直接選擇排序
//
void select_sort(int* array, int length)
{
assert(array && length >= 0);

int i, j, k, temp;

for (i = 0; i < length - 1; ++i) {
k = i;

for (j = i + 1; j < length; ++j) {
if (array[j] < array[k]) {
k = j;
}
}

if (k != i) {
temp = array[i];
array[i] = array[k];
array[k] = temp;
}
}
}

 

時間複雜度分析:

無論待排序記錄的初始狀態如何,在第 i 趟排序中選出最小關鍵字的記錄,需做 n - i 次比較,因此,總的比較次數為: n * (n - 1) / 2 =  O(n ^ 2),當待排序記錄的初始狀態為正序時,移動次數為 0;當初始狀態為反序時,每趟排序均要執行交換操作,總的移動次數取最大值 3 * (n-1)。所以,直接選擇排序的平均時間複雜度為  O(n ^ 2)。

 

空間複雜度:

很明顯, O(1)。

 

補充:

直接選擇排序是一個就地排序,且是非穩定排序。

 

堆排序

堆的定義:滿足如下約束的 n 個關鍵字序列 Kl,K2,…,Kn 稱為堆,1 ≤ i ≤ n/2,
   (1) ki ≤ K2i 且 ki ≤ K2i+1 (小根堆)   或

(2) Ki ≥ K2i 且 ki ≥ K2i+1 (大根堆)


從定義來看,堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉結點的關鍵字均不大於(或不小於)其左右孩子(若存在的話)結點的關鍵字。堆排序就是充分利用堆這種堆頂記錄要麼是最大的要麼是最小的有序性質,每次在堆中選擇最大或最小關鍵字完成排序。堆排序也是選擇排序的一種,它比直接選擇排序平均效率要高,因為直接選擇排序,每次比較之後並沒有對比較結果進行儲存,導致可能存在重複的比較,而堆則利用其堆性質將比較結果儲存在堆中了(非葉子節點的左邊孩子一定不大於或不小於比右邊的孩子)。


堆排序的關鍵就在於將待排序記錄的建立成堆,建好堆之後,將無序區的堆頂記錄(總是無序區的最大或最小)和無序區最後一個記錄交換,交換之後,無序區的最後一個記錄變成有序區的第一個記錄,而無序區新的的堆頂記錄不一定滿足堆性質了,因而需要將無序區調整而堆。如此迴圈,直到無序區為空白,結束排序。


所以堆排序的主要步驟就分三步:

1,建立初始堆;

2,將無序的堆頂記錄與最後一個記錄交換,縮小無序區;

3,將交換之後的無序區調整為堆,重複步驟 2。


代碼實現:

// 篩選法調整堆,除 [low] 之外,[low] 的兩個孩子均已是大根堆
void adjust_heap(int* heap, int low, int high)
{
assert(heap);

#if 1 // 迴圈實現

int i = low;
int j = 2 * i;
int temp = heap[i];

while (j <= high) {
// 若有兩個孩子,j 為孩子中大的那個的下標
if (j < high && heap[j] < heap[j + 1]) {
j = j + 1;
}

// 已是堆
if (temp >= heap[j]) {
break;
}

// 繼續篩選
else {
heap[i] = heap[j];
i = j;
j = 2 * i;
}
}

heap[i] = temp;

#else // 遞迴實現

int i = low;
int j = 2 * i;
int temp = heap[i];

if (j >= high) {
return;
}

// 若有兩個孩子,j 為孩子中大的那個的下標
if (j < high && heap[j + 1] > heap[j]) {
j = j + 1;
}

// 已經為堆,無需調整
if (heap[low] >= heap[j]) {
return;
}

heap[i] = heap[j];
heap[j] = temp;

// 調整之後,[j, high] 可能不滿足堆了,需繼續調整
adjust_heap(heap, j, high);

#endif
}

// 只有一個結點的樹是堆,而在完全二叉樹中,所有序號 i > n/2 的結點都是葉子,
// 因此以這些結點為根的子樹均已是堆。這樣,我們只需依次將以序號為
// n/2, n/2 - 1, …, 0 的結點作為根的子樹都調整為堆即可。
void build_heap(int* heap, int length)
{
assert(heap && length >= 0);

int i;

for(i = length / 2; i >= 0; --i) {
adjust_heap(heap, i, length - 1);
}
}


// 堆排序
//
void heap_sort(int* array, int length)
{
assert(array && length >= 0);

if (length <= 1) {
return;
}

int i, temp;

// 將 [0, length - 1] 建成初始堆
build_heap(array, length);

// 對當前無序區 [0, i - 1] 進行堆排序,共做 length - 1 趟。
for(i = length - 1; i > 0; --i) {
// 將堆頂和堆中最後一個記錄交換
temp = array[0];
array[0] = array[i];
array[i]= temp;

// 將 [0, i - 1] 重新調整為堆,僅有 [0] 可能違反堆性質
adjust_heap(array, 0, i - 1);
}
}


時間複雜度分析: 

堆排序的時間開銷主要由建立初始堆和反覆調整堆這兩部分的時間開銷構成,你可以想象成二叉樹的情形。堆排序的平均時間複雜度為O(nlgn)。

 

空間複雜度分析:

很明顯,為 O(1)。


補充:

堆排序是不穩定的就地排序。


=======================================================================================

測試:

在前文《排序演算法之插入排序》測試代碼的基礎上添加兩行代碼即可:

 

SortFucntionInfo sort_function_list[] = {

{"直接選擇排序", select_sort},

{"堆排序", heap_sort},

{"", NULL}

};

 

運行結果:

 

=== 直接選擇排序 ===

 original: 65 32 49 10 8 72 27 42 18 58 91

   sorted: 8 10 18 27 32 42 49 58 65 72 91

 

 original: 10 9 8 7 6 5 4 3 2 1 0

   sorted: 10 0 1 2 3 4 5 6 7 8 9

 

=== 堆排序 ===

 original: 65 32 49 10 8 72 27 42 18 58 91

   sorted: 8 10 18 27 32 42 49 58 65 72 91

 

 original: 10 9 8 7 6 5 4 3 2 1 0

   sorted: 0 1 2 3 4 5 6 7 8 9 10

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.