STL heap部分源代码分析
本文假设你已对堆排序的算法有主要的了解。
要分析stl中heap的源代码的独到之处。最好的办法就是拿普通的代码进行比較。话不多说,先看一段普通的堆排序的代码:
//调整大顶堆。使得结构合理
void max_heap(int a[],int node,int size)
{
int lg=node;
int l=node*2;
int r=node*2+1;
if(l<=size&&a[lg]<a[l])
{
lg=l;
}
if(r<=size&&a[lg]<a[r])
{
lg=r;
}
if(lg!=node)
{
//a[lg]=a[lg]^a[node];//交换
//a[node]=a[lg]^a[node];
//a[lg]=a[lg]^a[node];
int tt=a[lg];
a[lg]=a[node];
a[node]=tt;
max_heap(a,lg,size);
}
}
//生成一个大顶堆
void make_heap(int a[],int size)
{
for(int i=size/2;i>0;i--)
{
max_heap(a,i,size);
}
}
//堆排序,使数据在数组中按从小到大的顺序排列
void heap_sort(int a[],int size)
{
make_heap(a,size);
for(int i=1;i<size;i++)
{
int tt=a[1];
a[1]=a[size-i+1];
a[size-i+1]=tt;
max_heap(a,1,size-i);
}
}
相应第一个函数max_heap(),stl中有一个功能相似的函数adjust_heap(),也是调整整个heap,使之符合大顶堆的要求,代码例如以下:
// ============================================================================
// 保持堆的性质
//整个的过程是从根节点開始,将根节点和其子节点中的最大值对调,一直到叶节点为止(称之为下溯)
//然后。再从这个根节点開始。把它与其父节点进行比較,假设比父节点大,则父子节点对调。直到根节点为止(称之为上溯)
//============================================================================
// first 起始位置
// holeIndex 要进行调整操作的位置
// len 长度
// value holeIndex新设置的值
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) {
// 假设右孩子节点的值比左孩子节点的值要小。那么secondChild指向左孩子
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;
}
// 针对节点topIndex调用push_heap操作
__push_heap(__first, __holeIndex, __topIndex, __value);
}
//上溯
__push_heap(_RandomAccessIterator __first,
_Distance __holeIndex, _Distance __topIndex, _Tp __value)
{
// 获取父节点的索引值
_Distance __parent = (__holeIndex - 1) / 2;
// 假设还没有上升到根节点,且父节点的值小于待插入节点的值
while (__holeIndex > __topIndex && *(__first + __parent) < __value) {
// 父节点下降到holeIndex
*(__first + __holeIndex) = *(__first + __parent);
// 继续往上检查
__holeIndex = __parent;
__parent = (__holeIndex - 1) / 2;
}
// 插入节点
*(__first + __holeIndex) = __value;
}
stl里算法的堆的调整的主要流程如凝视里所说,主要是先进行下溯。直到叶节点,然后用push_heap进行上溯才调增完毕。对照之前的普通代码,主要有3点改变:
把普通代码的递归操作变成了循环操作。这点非常好理解,由于递归须要系统使用资源来维护递归栈,开销比較大,所以stl中除了sort的快排之外(由于快排的递归深度有限制)。一般都会把递归的算法转换成循环来做。
普通代码中,我们直接比較根节点和左右节点的值。然后假设须要的话跟节点直接和较大的节点交换,这样仅仅须要从上到下一趟比較。就能完毕树的调整。而stl的代码中,则要先下溯。然后再上溯。两趟才干完毕调整,看起来反而效率更低了,为什么呢?我细致分析了代码,感觉可能有一下两点的原因:1.代码的复用,由于下溯的主要作用事实上在保证大顶堆性质的前提下,让要调整的那个节点从根节点開始下沉。造成了一个新插入节点的假象。而push_heap()正是为了应对新插入节点而写的一个函2.数。这就复用了这块的代码。
2.效率上的一点提升,由于下溯的过程仅仅须要两个子节点的一次比較和根节点的一次赋值,而上溯的过程也仅仅须要与根节点的一次比較和子节点的一次赋,所以合起来事实上是两次赋值和两次比較;而普通的代码中,假设不考虑边界检查。找出三个点里的最大值,并与之交换,则至少须要两次比較,和三次赋值,所以stl的算法中,赋值运算少了一次,效率有所提升。
普通代码中,每次都须要对左子节点和右子节点进行边界的检查。而stl代码中。仅仅对右子节点进行边界检查,为了防止右子节点越界而左子节点没有越界的情况发生,在循环结束后添加了对左子节点的边界检查。这一改动大幅度降低了边界检查的次数,明显提升了效率。
通过上述的分析,事实上我们也能够以小见大。
事实上stl中,存在着大量这种优化,递归转循环,降低边界检查,用赋值取代交换等等。假设我们能细致研究,并在平时的编码中也养成这种习惯,就能极大得提升代码的效率。
STL heap部分源代码分析的更多相关文章
- STL源代码分析——STL算法sort排序算法
前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SG ...
- STL源代码分析——STL算法remove删除算法
前言 因为在前文的<STL算法剖析>中,源代码剖析许多.不方便学习,也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的remove删除算法. ...
- STL源代码分析——STL算法merge合并算法
前言 因为在前文的<STL算法剖析>中.源代码剖析许多.不方便学习.也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的merge合并算法. ...
- Android应用程序进程启动过程的源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址: http://blog.csdn.net/luoshengyang/article/details/6747696 Android 应用程序框架层创 ...
- Parrot源代码分析之海贼王
我们的目的是找到speedup-example在使用Parrot加速的原因,假设仅仅说它源于Context Switch的降低,有点简单了,它究竟为什么降低了?除了Context Switch外是否还 ...
- 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t
nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...
- JAVA随笔篇一(Timer源代码分析和scheduleAtFixedRate的使用)
写完了基础篇,想了非常久要不要去写进阶篇.去写JSP等等的用法.最后决定先不去写.由于自己并非JAVA方面的大牛.眼下也在边做边学,所以决定先将自己不懂的拿出来学并记下来. Timer是Java自带的 ...
- HBase源代码分析之HRegionServer上MemStore的flush处理流程(一)
在<HBase源代码分析之HRegion上MemStore的flsuh流程(一)>.<HBase源代码分析之HRegion上MemStore的flsuh流程(二)>等文中.我们 ...
- HBase源代码分析之HRegionServer上MemStore的flush处理流程(二)
继上篇文章<HBase源代码分析之HRegionServer上MemStore的flush处理流程(一)>遗留的问题之后,本文我们接着研究HRegionServer上MemStore的fl ...
随机推荐
- JAVA输出带BOM的UTF-8编码的文件
当从http 的response输出CSV文件的时候,设置为utf8的时候默认是不带bom的,可是windows的Excel是使用bom来确认utf8编码的,全部须要把bom写到文件的开头. 微软在 ...
- HTML5_Web_SQL_Database数据库
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiEAAAEGCAIAAAA8NNLDAAAgAElEQVR4nOxdd5hcVdk/SUgghCSEhC ...
- bzoj1193: [HNOI2006]马步距离(贪心+bfs)
1193: [HNOI2006]马步距离 题目:传送门 题解: 毒瘤题... 模拟赛时的一道题,刚开始以为是一道大难题...一直在拼命找规律 结果.... 还是说正解吧: 暴力的解法肯定是直接bfs, ...
- bzoj1051: [HAOI2006]受欢迎的牛(强联通)
1051: [HAOI2006]受欢迎的牛 题目:传送门 题解: 今天又做一道水题... 强联通啊很明显 水个模板之后统计一下每个强联通分量中点的个数,再统计一下出度... 不难发现:缩点之后当且仅当 ...
- flatMap作用
总结:1. map会将每一条输入映射为一个新对象.{苹果,梨子}.map(去皮) = {去皮苹果,去皮梨子} 其中: “去皮”函数的类型为:A => B 2.flatMap包含两个操作:会将每一 ...
- windows tensorflow 版本与升级
tensorflow 的版本在 1.1.0/1.2.0 之后 api 迎来重大变化,有必要将版本升级到最新的 1.1.0 以上. 1. 使用 upgrade CPU:pip3 install –upg ...
- Linux-php7安装redis
Linux-php7安装redis 标签(空格分隔): 未分类 安装redis服务 1 下载redis cd /usr/local/ 进入安装目录 wget http://download.redis ...
- IDEA中FindBugs编码规范工具使用
IDEA中安装FindBugs插件: file--Settings--Plugins 在Plugins搜索FindBugs: 安装完成后在项目中选中文件右键找到findBugs: 检查代码结果: 按照 ...
- ios发布以后关键信息确认与nslog
发布以后信息查看的路径: xcode->window->devices and …->查看如图的log. 通常在发布以后,处于安全和性能的考虑,会禁止打印log:但是在关键的信息需要 ...
- Xrdp - 通过Windows的RDP连接Linux远程桌面(Ubuntu/CentOS/Redhat 7)(转载)
您多久访问一次Linux桌面? 您使用什么工具来访问远程桌面? Xrdp是一个开源工具,允许用户通过Windows RDP访问Linux远程桌面. 除了Windows RDP之外,xr ...