STL之heap的make_heap函数

在看侯捷翻译的STL源码剖析时,发现关于heap这一节点错误,特此指出.


1 make_heap源码

template <class _RandomAccessIterator>
inline void 
make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
  __make_heap(__first, __last,
              __VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
}

make_heap用的是__make_heap函数.


2 __make_heap源码

template <class _RandomAccessIterator, class _Tp, class _Distance>
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--;
  }
}

这是建heap的标准过此,即从(n-1)/2 到 0, 调用__adjust_heap进行最大堆性质的保持.注: n为最后一个元素的下标(0 , 1, ... n).


3 __adjust_heap源码

template <class _RandomAccessIterator, class _Distance, class _Tp>
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);
}

这个函数比较关键,与我们常写的的方法不太一样.假设当前节点为p, 通常的方法是: 

  1. 找到p的左右孩子(如果有)中最大值m
  2. 若p的值大于m,没有破坏最大堆性质,直接退出.
  3. 若p的值小于m, 则交互p与最大的孩子的值,同时p指向交换的孩子,继续第一步.

而上面的__adjust_heap并没有父节点与最大孩子节点值的比较,而是直接给父节点赋值最大孩子的值,并下溯.

唯一的不同在于__adjust_heap调用了__push_heap函数.

然而侯捷指出:


可以将__push_heap改为*(first+holeIndex) = value;一下子然我蒙啦,想了许久都没想通.

4 __push_heap源码

template <class _RandomAccessIterator, class _Distance, class _Tp>
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;
}

__push_heap相当于在堆中插入一个元素.即对空洞位置的调整.不过,为什么追求高效的STL要这么写呢?

如果对一个已是最大堆的堆再调用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为什么这么写,如果有读者知道,请给我留言.






  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值