一、什麼是堆?
堆是一種資料結構,是一種特殊的二叉樹。堆安排在乙個連續的陣列中,這個陣列的下標從 1 開始,節點 i 的子節點下標可以用 i * 2, i * 2 + 1 計算得到。
例如,3 的子節點就是 3 * 2 = 6, 3 * 2 + 1 = 7。
此外,節點直接還滿足這樣乙個性質:父節點總是【大於/小於】它的兩個子節點。父節點較大的稱為大根堆,反之為小根堆。子節點之間沒有大小關係的要求。
二、如何建堆?
對於只有乙個元素的陣列,很顯然它不需要任何操作,就是乙個堆。
以大根堆為例,現在兩邊的子樹都是乙個合法的大根堆了,如何調整 4 的位置呢?答案是讓 4 下沉,並讓數值最大的子節點上浮。
但是這樣的話,左邊這個堆的性質又被破壞了。我們如法炮製,繼續讓 4 下沉。
這樣,我們就得到了乙個合法的堆。
現在的問題就是如何把這個下沉操作應用到整個陣列,使其成為堆。
解決方法如下:
1. 對於葉子節點,顯然它是乙個合法的堆。
2. 我們從最後乙個非葉節點開始,對這些節點進行下沉操作。這相當於在包含非葉節點的倒數第一層開始。
3. 由於是包含非葉節點的倒數第一層,我們可以確保我們操作的節點全部具備了左右子樹均為合法堆的性質。
4. 接著我們會前往包含非葉節點的倒數第二層。當我們在這一層的時候,底下的這一層已經全部是調整好的節點了。因此,我們可以逐個對它們進行下沉。
5. 這樣子,我們就能得到乙個完全的堆。
void heapify(vector &a, int三、如何堆排序index)
//我們確保了 index 是非葉節點,
//因此至多只需要檢查右節點是不是空。
if (rightindex >=a.size())
}else
if (a[rightindex] >a[maxindex])
if (maxindex !=index)
}}void makeheap(vector&a)
}
當我們構建了大根堆之後,我們就可以提取堆的根,知道整個陣列的最大值。
而取了這個節點之後,誰來當根?當了之後是怎麼維護堆的性質的?
考慮到整個堆除了沒有根之外,都是合法大根堆根節點,我們只需要把陣列的最後一位拿出來放在堆頂。此時我們的堆就變成了「除了根節點,其它的節點都是合法」。
只需要經過提取 -> 調換 -> 維護 -> 提取 -> ... 這樣的迴圈,我們就可以得到乙個排序好的陣列。
然而這樣的話我們會有額外的 o(n) 空間開銷,因此比起把提取的根節點放到乙個新的陣列裡,我們不如直接置換到堆的末尾。
這樣我們就完成了整個堆排序的演算法。
//注意:為了完成堆排序,heapify 函式額外新增了乙個當前堆長度的引數。
void heapify(vector &a, int index, int
maxlen)
//我們確保了 index 是非葉節點,
//因此至多只需要檢查右節點是不是空。
if (rightindex >=maxlen)
}else
if (a[rightindex] >a[maxindex])
if (maxindex !=index)
}}void makeheap(vector&a)
}void heapsort(vector&heap)
}
堆排序 堆排序優化 索引堆排序
堆排序 堆排序優化 索引堆排序 注 堆排序 索引堆排序 都是不穩定的排序。注 索引最大堆排序有誤!有沒有大神可以指點一二?1 堆 所有元素 都從索引0開始 父親結點索引 i 左孩子結點索引 2i 1 右孩子結點索引 2i 2 左後乙個非葉子結點索引 n 1 2 用於構建堆,從最後乙個非葉子結點索引開...
堆排序 堆排序優化 索引堆排序
堆排序 堆排序優化 索引堆排序 注 堆排序 索引堆排序 都是不穩定的排序。注 索引最大堆排序有誤!有沒有大神可以指點一二?1 堆 所有元素 都從索引0開始 父親結點索引 i 左孩子結點索引 2i 1 右孩子結點索引 2i 2 左後乙個非葉子結點索引 n 1 2 用於構建堆,從最後乙個非葉子結點索引開...
堆排序 模擬堆排序
838.堆排序 輸入乙個長度為n的整數數列,從小到大輸出前m小的數。輸入格式 第一行包含整數n和m。第二行包含n個整數,表示整數數列。輸出格式 共一行,包含m個整數,表示整數數列中前m小的數。資料範圍 1 m n 1051 m n 105,1 數列中元素 1091 數列中元素 109 輸入樣例 5 ...