heap最大(小)堆

首先是概念,堆属于完全二叉树。

完全二叉树百度百科描述是:

(1)若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数。

(2)第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

最大堆对于完全二叉树的节点数值要求是:

根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。也就是说左右子树也是一个大顶堆。

堆一般通过数组表示。从上到下从左到右一个个排列。

先给个我们使用heap的例子

#include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
int main()
{
  vector<int> v;
  v.reserve(16);
  v.push_back(1);
  v.push_back(5);
  v.push_back(8);
  v.push_back(6);
  v.push_back(7);
  make_heap(v.begin(),v.end());       //先创建一个大顶堆再说
  for(int i = 0; i < v.size(); ++i)
  {
    cout << v[i] << " ";
  }
  cout << endl;                       //输出结果 8 7 1 6 5 最大的8到最前面去了吧~

  pop_heap(v.begin(),v.end());        //吧最大那个就是第一个弄出来,vector里的值会删除吗??不会
  for(int i = 0; i < v.size(); ++i)
  {
    cout << v[i] << " ";
  }
  cout << endl;                      //输出结果 7 6 1 5 8   看到了吧,把8放到最后去啦~~前面的依旧重新自己调整为大顶堆
  pop_heap(v.begin(),v.end()-1);     //把7也取出来吧 如果再也不想看到8,你就把vector最后一个erase掉
  for(int i = 0; i < v.size(); ++i)
  {
    cout << v[i] << " ";
  }
  cout << endl;                     //输出结果 6 5 1 7 8 
  v.push_back(1000);
 push_heap(v.begin(),v.end());    //输出结果 1000 5 6 7 8 1   (先往vector尾端插入再push_heap)
}
记住两点

(1)pop_heap发生在开始位置(它最大嘛),push_heap在最后。

(2)pop和push前都得保证它是一个heap啊!都不是一个heap了操作它也没意义了啊
一直pop_heap会怎么样啊?对,就是heap从小到大排序。下面是STL sort_heap实现代码:

sort_heap(_RandomAccessIterator __first,
          _RandomAccessIterator __last, _Compare __comp)
{
  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
  while (__last - __first > 1)
    pop_heap(__first, __last--, __comp);
}

那么如何创建出一个大顶堆。(需要小顶堆可传入compare比较函数)

template <class _RandomAccessIterator, class _Tp, class _Distance>
void 
__make_heap(_RandomAccessIterator __first,
            _RandomAccessIterator __last, _Tp*, _Distance*)
{
  if (__last - __first < 2) return;         //没有或者只有1个元素不用排序了
  _Distance __len = __last - __first;       //有多少个元素保存起来,作越界判断
  _Distance __parent = (__len - 2)/2;      //从后往前开始,取出最后一个parent
  while (true) {
   //把这个value放到一个合适的位置。先放着略过,等下分析这个
  __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent))); 
  if (__parent == 0) return; 
  __parent--; //一个个节点来吧,感觉好费啊
 }
}


 最最重要的函数 
__adjust_heap,功能是把当前节点子树调整为一个大顶堆树。 

它主要的思想是这样的:

把要调整的节点的值先取出来,就是这个参数value,然后把这个节点当做空节点,把max(left_child,right_child)大的孩子值放入这个位置,然后位置下移一层(二叉树的度),赋值的那个孩子再次当做parent,他的值已经给上一层了,所以他变成空节点了,他也得从孩子节点选大的来填充,如此循环下去到最大深度的叶子节点,child就都小于等于parent啦~~

还有一个最开始的value呢?最后把这个value放到最下层的那个空节点,然后让他一点一点往上和parent比较往上移动吧

template <class _RandomAccessIterator, class _Distance, class _Tp>
void 
__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
              _Distance __len, _Tp __value)
{
  _Distance __topIndex = __holeIndex;                           //把这个index当做根节点(子树)
  _Distance __secondChild = 2 * __holeIndex + 2;                //把right_child取出来
  while (__secondChild < __len) {
    if (*(__first + __secondChild) < *(__first + (__secondChild - 1))) //left_child和right_child选出大的
      __secondChild--;
    *(__first + __holeIndex) = *(__first + __secondChild);     //胜者才有资格放入parent
    __holeIndex = __secondChild;
    __secondChild = 2 * (__secondChild + 1);                  //继续往下遍历right_child吧
  }
  //_seondChild是0开始的下标,最后一个叶子节点的处理,如果只有left_child才会出现等于
  if (__secondChild == __len) 
  { 
    *(__first + __holeIndex) = *(__first + (__secondChild - 1)); //没right_child和你争了
     __holeIndex = __secondChild - 1; 
  } 
  __push_heap(__first, __holeIndex, __topIndex, __value); //一层一层往上爬。 
 //(注侯捷老师的注解 可换成*(first + holeIndex) = value,有误,不能换。make_heap没问题但是pop_heap有问题)
}

</pre><pre>
看看怎么爬的吧。。

__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);          //战胜parent把他换下来~~
    __holeIndex = __parent;
    __parent = (__holeIndex - 1) / 2;
  }    
  *(__first + __holeIndex) = __value;                        //最后终于找到合适的位置了
}

pop_heap也贴一下

template <class _RandomAccessIterator, class _Tp>
inline void 
__pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last,
               _Tp*)
{
  __pop_heap(__first, __last - 1, __last - 1, 
             _Tp(*(__last - 1)), __DISTANCE_TYPE(__first)); //*(last-1)把最后那个值取出来

}

template <class _RandomAccessIterator, class _Tp, class _Distance>
inline void 
__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
           _RandomAccessIterator __result, _Tp __value, _Distance*)
{
  //把最大值放到最后去,最后那个值是已经取出来过的(__value)~不怕
  *__result = *__first;
  //都去抢那个空位置去吧,该函数最后一行处理一下这个可能真的是很大的value哦
  __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value);
 }

 个人总结:挖空节点一个再处理,把它保存起来,调动其他节点的处理。make_heap感觉有点费,make_heap while遍历处理节点比较过多。 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值