前一段時間師姐在看大話數據結構這本書,當看到堆排序時她問我,當時我覺得堆排序很簡單,無非就是堆頂和堆尾對換,並輸出最后一個,剩下的進行堆調整再一次次循環下去。但是她又問道怎么實現堆調整,當時有點路子,但是當我真正想實現的時候,卻出現了很多問題,正好最近正在寫一些排序算法,所以今天就詳細說說堆排序的具體。
堆:
堆是一個完全二叉樹,分有最大堆和最小堆;它的每個節點值總比每個孩子值都大(這是最大堆,最小堆反之,下面提到得堆均為最大堆),並且最后一層處於左側。
下面我們說說初始化堆:利用數組實現
如下圖所示,我們先把數組{1,2,3,5,2,3}化為一個完全二叉樹(堆),然后在對其進行堆調整,這里我們只談最大堆,所以我們可以利用每個節點值總比每個孩子值都大這個性質來進行調整。
下面給出初始化堆的代碼:
void max_HeapAdjust(int* A, int index, int n) { //最大堆 if (2 * index < n){ //這個條件可以跳出遞歸 int cur = index; //用cur來記錄結點值和孩子值比較大的那個值得下標 if (2 * index + 1 < n && A[2 * index + 1] > A[cur]) //我當時用了index,不好實現,雖然測試過了幾個例子,但是還是用來記錄比較好 cur = 2 * index + 1; if (2 * index + 2 < n && A[2 * index + 2] > A[cur]) cur = 2 * index + 2; if (cur != index){ swap(A[cur], A[index]); //孩子大的那個與結點互換。 max_HeapAdjust(A, cur, n); } } } //從 n / 2 - 1 往前循環是因為從這個值開始往后面就沒有子節點了,我們這里只考慮有孩子的。 int* bulitHeap(int* A, int n) { for (int i = n / 2 - 1 ; i >= 0; i--) max_HeapAdjust(A, i, n);return A; }
我用了上圖的中數組進行了測試,顯示正確。
接下來我們就進入了堆排序的實現了,我們還是以上面那個{1,2,3,5,2,3}的例子來實現。
首先我們談談堆排序的思想:將待排序的序列轉化成為一個堆;此時將堆頂的元素與堆尾的元素交換,並且輸出對換后堆尾的數據,並將其移除;然后將剩余的n-1序列進行調整之后再首尾對調輸出移除,如此反復下去。知道只有一個節點,直接將其輸出。
在實現過程中我們把交換之后的元素放在數組的尾部,並且對剩下n-1個元素進行調整在交換,剩下n-2個。。。如此反復下去。
經過上面的變化過程我們可以發現,我們在給一個有序堆進行調整的時候,應該不用在進行一些循環建堆的過程,我們只需要進行上面代碼中的一次max_HeapAdjust調整即可,因為堆除了堆頂其他都是有序的,那么我們只有一條線走到底調整即可。
下面給出測試的完整代碼:
#include<iostream> using namespace std; void swap(int& a, int& b) { int temp = a; a = b; b = temp; } //堆調整算法實現 void max_HeapAdjust(int* A, int index, int n) { if (2 * index < n){ int cur = index; if (2 * index + 1 < n && A[2 * index + 1] > A[cur]) cur = 2 * index + 1; if (2 * index + 2 < n && A[2 * index + 2] > A[cur]) cur = 2 * index + 2; if (cur != index){ swap(A[cur], A[index]); max_HeapAdjust(A, cur, n); } } } int* heapSort(int* A, int n) { for (int i = n / 2 -1 ; i >= 0; i--) //該循環建堆 max_HeapAdjust(A, i, n); for (int j = n -1 ; j > 0; j--) { swap(A[j], A[0]); max_HeapAdjust(A, 0, j); //堆調整 } return A; } int main() { const int n = 6; int a[n] = { 1, 2, 3, 5, 2, 3 }; for (int i = 0; i != n; ++i) cout << a[i] << " "; cout << endl; heapSort(a, n); for (int i = 0; i != n; ++i) cout << a[i] << " "; cout << endl; return 0; }
堆排序在排序算法中的效果還是很好的,所以還是自己實現出來比較好。
聲明:本篇文章參考了<<大話數據結構>>以及牛客網上的一些思路。