heap最大(小)堆
2016-08-27 00:03
274 查看
首先是概念,堆属于完全二叉树。
完全二叉树百度百科描述是:
(1)若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数。
(2)第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
最大堆对于完全二叉树的节点数值要求是:
根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。也就是说左右子树也是一个大顶堆。
堆一般通过数组表示。从上到下从左到右一个个排列。
先给个我们使用heap的例子
(1)pop_heap发生在开始位置(它最大嘛),push_heap在最后。
(2)pop和push前都得保证它是一个heap啊!都不是一个heap了操作它也没意义了啊
一直pop_heap会怎么样啊?对,就是heap从小到大排序。下面是STL sort_heap实现代码:
那么如何创建出一个大顶堆。(需要小顶堆可传入compare比较函数)
最最重要的函数__adjust_heap,功能是把当前节点子树调整为一个大顶堆树。
它主要的思想是这样的:
把要调整的节点的值先取出来,就是这个参数value,然后把这个节点当做空节点,把max(left_child,right_child)大的孩子值放入这个位置,然后位置下移一层(二叉树的度),赋值的那个孩子再次当做parent,他的值已经给上一层了,所以他变成空节点了,他也得从孩子节点选大的来填充,如此循环下去到最大深度的叶子节点,child就都小于等于parent啦~~
还有一个最开始的value呢?最后把这个value放到最下层的那个空节点,然后让他一点一点往上和parent比较往上移动吧
pop_heap也贴一下
个人总结:挖空节点一个再处理,把它保存起来,调动其他节点的处理。make_heap感觉有点费,make_heap while遍历处理节点比较过多。
完全二叉树百度百科描述是:
(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遍历处理节点比较过多。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C++的template模板中class与typename关键字的区别分析
- C与C++之间相互调用实例方法讲解