什么是堆?
如圖所示:當(dāng)父結(jié)點(diǎn)的值,都比其子結(jié)點(diǎn)的值小時(shí),就是小根堆,一個(gè)小根堆中,最小的值肯定在堆頂。
同理,當(dāng)父結(jié)點(diǎn)的值都比其子結(jié)點(diǎn)的值大時(shí),就是大根堆,一個(gè)大根堆中,最大的值肯定在堆頂。
堆排序
現(xiàn)在是一個(gè)大頂堆,那么最大值肯定在堆頂處,我們將堆頂?shù)脑嘏c最后一個(gè)元素交換。
然后再將剩下的元素(排除交換的最后一個(gè)元素)重新調(diào)整為大根堆,過程如下:
10 與左兒子結(jié)點(diǎn)右兒子結(jié)點(diǎn)中最大值交換。
此時(shí)有形成一個(gè)新的大根堆。我們?cè)谧龅谝徊阶?/p>
此時(shí),我們就將最大數(shù),和第二大數(shù)排好序(升序排列)。
將這個(gè)過程往復(fù)下去,最后我們就能夠?qū)⑺袛?shù)據(jù)排序完畢。
我們上面所作的所有步驟,都是假設(shè)序列一開始就是大根堆的情況下,但是并不是所有將要排序序列一開始就是是符合大根堆的。
所以,其中關(guān)鍵就是構(gòu)造大根堆。當(dāng)我們第一次把隨機(jī)的數(shù)組構(gòu)造成大根堆后,以后我們只需要每次交換后調(diào)整堆頂?shù)臄?shù)據(jù)使之再成為大根堆。往復(fù)下去。
思路:
- precolate down(下溯算法):
給定一個(gè)結(jié)點(diǎn)n, 其左子結(jié)點(diǎn)2n+1,右子節(jié)點(diǎn):2n+2。然后與左右子結(jié)點(diǎn)中最大值childmax比較,如果大于childmax,則說明該結(jié)點(diǎn)及子樹已經(jīng)符合大根堆標(biāo)準(zhǔn)。
否則則與childmax交換,然后再把原childmax結(jié)點(diǎn)作為新的父結(jié)點(diǎn)和其左子結(jié)點(diǎn)和右子結(jié)點(diǎn)比較,作上述過程。最后一直下溯至葉子結(jié)點(diǎn)(葉子節(jié)點(diǎn)沒有左右子結(jié)點(diǎn))。這樣我們將結(jié)點(diǎn)n及其子樹調(diào)整為了大根堆。整個(gè)過程如上圖3。
但是步驟3排序思路的大前提是我們已經(jīng)將一個(gè)隨機(jī)的數(shù)組已經(jīng)有構(gòu)造成大根堆的情況下。所以我們之前還必須做將一個(gè)隨機(jī)數(shù)組構(gòu)造成一個(gè)大根堆。
- 那怎么做將一個(gè)隨機(jī)數(shù)組構(gòu)造成一個(gè)大根堆?
有了步驟3 的思路,我們可以從最后一個(gè)非葉子結(jié)點(diǎn)開始,每一個(gè)按照步驟3調(diào)整,(這樣,每一個(gè)結(jié)點(diǎn)調(diào)整后都可以保證該結(jié)點(diǎn)及其子樹是副符合大根堆)。這樣至下而上,直到根節(jié)點(diǎn)。完成后我們將一個(gè)隨機(jī)數(shù)組調(diào)整為了大根堆。
代碼實(shí)現(xiàn)
// precolate down 將[n, lastIndex]的值調(diào)整為大根堆。
void HeapAdjust(vector<int> &vec, int n, int lastIndex)
{
int leftChild = n * 2 + 1; // 左兒子結(jié)點(diǎn)
int rightChild = n * 2 + 2; // 右兒子結(jié)點(diǎn)
int lastNotLeave = (lastIndex-1)/2; // 最后一個(gè)非葉子結(jié)點(diǎn)。
/* 如果當(dāng)前結(jié)點(diǎn)不是葉子結(jié)點(diǎn), 則調(diào)整 */
if (n <= lastNotLeave)
{
/* 左右子節(jié)點(diǎn)較大值結(jié)點(diǎn)的索引 */
int maxIndex;
if (rightChild <= lastIndex) // 該父結(jié)點(diǎn)有左右2個(gè)子結(jié)點(diǎn)
maxIndex = vec[leftChild] >= vec[rightChild] ? leftChild : rightChild;
else
maxIndex = leftChild;
/* 與當(dāng)前結(jié)點(diǎn)值比較 */
if (vec[maxIndex] > vec[n])
{
swap(vec[maxIndex], vec[n]);
/* 如果當(dāng)左右子較大的結(jié)點(diǎn)不是是葉子結(jié)點(diǎn), 則繼續(xù)下溯調(diào)整 */
if (maxIndex <= lastNotLeave)
HeapAdjust(vec, maxIndex, lastIndex);
}
}
}
/* 第一次將一個(gè)隨機(jī)數(shù)組調(diào)整為大根堆 */
void makeHeap(vector<int>& vec, int length)
{
/* 最后一個(gè)子樹的父節(jié)點(diǎn)是(length-2)/2,從它開始上溯調(diào)整 */
for (int i = (length-2)/2; i >= 0; i--)
HeapAdjust(vec, i, length-1);
}
void HeapSort(vector<int>& vec, int length)
{
makeHeap(vec, length);
for (int i = length - 1; i > 0; i--)
{
swap(vec[i], vec[0]);
HeapAdjust(vec, 0, i-1); // 交換值之后,只需要調(diào)整第一個(gè)結(jié)點(diǎn)
}
}
性能
平均時(shí)間 | 最差 | 最佳 | 輔助空間 | 穩(wěn)定性 |
---|---|---|---|---|
O(Nlog2N) | O(Nlog2N) | ONlog2N) | O(1) | 不穩(wěn)定 |