STL原始碼分析之heap大根堆
阿新 • • 發佈:2018-12-08
前言
在分析本節之前你至少應該對堆排序有所瞭解, 大根堆, 小根堆等. 本節分析的heap
就是堆排序, 嚴格意義上來講heap並不是一個容器, 所以他沒有實現自己的迭代器, 也就沒有遍歷操作, 它只是一種演算法. 程式碼來自stl_heap.h.
heap分析
push插入元素
插入函式是push_heap
. heap
只接受RandomAccessIterator
型別的迭代器.
注意, 在分析heap的時候最好還是自己畫一次, 畫一個數組一個二叉樹.
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance* , T*)
{
// 這裡傳入的是兩個迭代器的長度, 0, 還有最後一個數據
__push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
}
push的核心程式碼
template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,Distance topIndex, T value)
{
// 這裡就行二分, 因為二叉樹每一行都是2的倍數
Distance parent = (holeIndex - 1) / 2;
// 這裡判斷的是當前沒有達到堆頂並且傳入的值大於根節點的值, 那就將根節點下移
while (holeIndex > topIndex && *(first + parent) < value) {
// 將根節點下移
*(first + holeIndex) = *(first + parent);
holeIndex = parent;
parent = (holeIndex - 1) / 2;
}
// 將陣列插入到合適的位置, 可能是根也可能是葉
*(first + holeIndex) = value;
}
pop彈出元素
pop操作其實並沒有真正意義去刪除資料, 而是將資料放在最後, 只是沒有指向最後的元素而已, 這裡arrary也可以使用, 畢竟沒有對陣列的大小進行調整. pop的實現有兩種, 這裡都羅列了出來, 另一個傳入的是cmp偽函式.
template <class RandomAccessIterator, class Compare>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last,
Compare comp) {
__pop_heap_aux(first, last, value_type(first), comp);
}
template <class RandomAccessIterator, class T, class Compare>
inline void __pop_heap_aux(RandomAccessIterator first,
RandomAccessIterator last, T*, Compare comp) {
__pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp,
distance_type(first));
}
template <class RandomAccessIterator, class T, class Compare, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
RandomAccessIterator result, T value, Compare comp,
Distance*) {
*result = *first;
__adjust_heap(first, Distance(0), Distance(last - first), value, comp);
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
RandomAccessIterator result, T value, Distance*) {
*result = *first; // 因為這裡是大根堆, 所以first的值就是最大值, 先將最大值儲存.
__adjust_heap(first, Distance(0), Distance(last - first), value);
}
pop的核心函式. 這裡主要之分析第一個版本.
pop彈出的是二叉樹的最下一排的資料.
template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value)
{
// holeIndex傳入的是0
Distance topIndex = holeIndex;
// secondChild是右孩子的一個節點
Distance secondChild = 2 * holeIndex + 2;
while (secondChild < len) {
// 比較左右節點, 根節點較下就將根節點下移, 比較大的節點上移
if (*(first + secondChild) < *(first + (secondChild - 1)))
secondChild--;
*(first + holeIndex) = *(first + secondChild);
holeIndex = secondChild;
// 下一個左右節點
secondChild = 2 * (secondChild + 1);
}
if (secondChild == len) {
// 沒有右節點就找左節點並且上移
*(first + holeIndex) = *(first + (secondChild - 1));
holeIndex = secondChild - 1;
}
// 重新調整堆
__push_heap(first, holeIndex, topIndex, value);
}
// cmpare版本只將比較修改成使用者定義的函式
template <class RandomAccessIterator, class Distance, class T, class Compare>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
Distance len, T value, Compare comp) {
Distance topIndex = holeIndex;
Distance secondChild = 2 * holeIndex + 2;
while (secondChild < len) {
if (comp(*(first + secondChild), *(first + (secondChild - 1))))
secondChild--;
*(first + holeIndex) = *(first + secondChild);
holeIndex = secondChild;
secondChild = 2 * (secondChild + 1);
}
if (secondChild == len) {
*(first + holeIndex) = *(first + (secondChild - 1));
holeIndex = secondChild - 1;
}
__push_heap(first, holeIndex, topIndex, value, comp);
}
make_heap函式, 將陣列變為堆存放.
template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last) {
__make_heap(first, last, value_type(first), distance_type(first));
}
template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
Distance*) {
if (last - first < 2) return;
// 計算長度, 並找出中間的根值
Distance len = last - first;
Distance parent = (len - 2)/2;
while (true) {
// 一個個進行調整, 放到後面
__adjust_heap(first, parent, len, T(*(first + parent)));
if (parent == 0) return;
parent--;
}
}
sort, 堆排序其實就是每次將第一位資料彈出從而實現排序功能.
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
while (last - first > 1) pop_heap(first, last--);
}
template <class RandomAccessIterator, class Compare>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last,
Compare comp) {
while (last - first > 1) pop_heap(first, last--, comp);
}
總結
heap
沒有自己的迭代器,只要支援RandomAccessIterator
的容器都可以作為Heap容器. heap
最重要的函式還是pop
和push
的實現.