在看侯捷翻译的STL源码剖析时,发现关于heap这一节点错误,特此指出.
template
inline void
make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
__make_heap(__first, __last,
__VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
}
template
void
__make_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Tp*, _Distance*)
{
if (__last - __first < 2) return;
_Distance __len = __last - __first;
_Distance __parent = (__len - 2)/2;
while (true) {
__adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));
if (__parent == 0) return;
__parent--;
}
}
template
void
__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __len, _Tp __value)
{
_Distance __topIndex = __holeIndex;
_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);
}
而上面的__adjust_heap并没有父节点与最大孩子节点值的比较,而是直接给父节点赋值最大孩子的值,并下溯.
唯一的不同在于__adjust_heap调用了__push_heap函数.
然而侯捷指出:
可以将__push_heap改为*(first+holeIndex) = value;一下子然我蒙啦,想了许久都没想通.
template
void
__push_heap(_RandomAccessIterator __first,
_Distance __holeIndex, _Distance __topIndex, _Tp __value)
{
_Distance __parent = (__holeIndex - 1) / 2;
while (__holeIndex > __topIndex && *(__first + __parent) < __value) {
*(__first + __holeIndex) = *(__first + __parent);
__holeIndex = __parent;
__parent = (__holeIndex - 1) / 2;
}
*(__first + __holeIndex) = __value;
}
如果对一个已是最大堆的堆再调用make_heap,举例:
最大堆: [3, 2, 1]
按STL的实现将会这样执行
1) 将父节点值3保存到变量value中,这样父节点将变成空洞节点记为p. [ p, 2, 1]
2) 找孩子节点的最大值,赋值给空洞节点p = 2,空洞节点p下溯到左孩子. [2, p, 1]
3) 再次计算左右孩子位子,超出范围,调用__push_heap函数,
4) 调整p的位置,又将2回归到原来位置, 退出while循环.[p, 2, 1]
5) 将value赋值到空洞节点. [3, 2, 1],完成.
本人觉得,还不如直接按照常规方法,在adjust_heap比较父节点和最大孩子节点的值.这样效率更高.
至于STL为什么这么写,如果有读者知道,请给我留言.