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为什么这么写,如果有读者知道,请给我留言.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-28 21:01:36

STL之heap的make_heap函数的相关文章

STL之heap

STL的堆操作 STL里面的堆操作一般用到的只有4个:make_heap();.pop_heap();.push_heap();.sort_heap();他们的头文件函数是#include <algorithm>首先是make_heap();他的函数原型是:void make_heap(first_pointer,end_pointer,compare_function);一个参数是数组或向量的头指针,第二个向量是尾指针.第三个参数是比较函数的名字.在缺省的时候,默认是大跟堆.(下面的参数都一

c++中STL之heap, priority_queue使用

一.heap heap并不属于STL容器组件,它分为 max heap 和min heap,在缺省情况下,max-heap是优先队列(priority queue)的底层实现机制.而这个实现机制中的max-heap实际上是以一个vector表现的完全二叉树(complete binary tree).STL在<algorithm.h>中实现了对存储在vector/deque 中的元素进行堆操作的函数,包括make_heap, pop_heap, push_heap, sort_heap,对不愿

STL之heap相关操作算法

说明:本文仅供学习交流,转载请标明出处,欢迎转载! 堆(heap)是一种非常重要的数据结构(这里我们讨论的是二叉堆),它是一棵满足特定条件的完全二叉树,堆的定义如下: 堆是一棵树完全二叉树,对于该完全二叉树中的每一个结点x,其关键字大于等于(或小于等于)其左右孩子结点,而其左右子树均为一个二叉堆. 在上述的定义中,若堆中父亲结点关键字的值大于等于孩子结点,则称该堆为大顶堆:若堆中父亲结点关键子的值小于等于孩子结点,则称该堆为小顶堆. 由于堆是一棵完全二叉树,所以我们可以很轻易地用一个数组存储堆中

STL中heap的使用方法

在STL中heap的用法主要是make_heap(),push_heap(),pop_heap()的用法.具体均在代码中: // range heap example  用heap构造出来的实际上是一棵树 #include <iostream> #include <algorithm> #include <vector> using namespace std; int main () { int myints[] = {10,20,30,5,15}; vector&l

编写STL中没有定义的函数(如果能编译)

实例目标:本例介绍如何编写标准模板库STL中没有定义的函数.这里列举两个例子,一个是将所有数据类型转换成字符串型的to_string函数,另一个是它的"反函数"from_string函数,用来将字符串转化为某一类型 具体内容: 在std::string模板中没有包含一些函数实用性较强的,原因就是因为客户轻易编写其中的代码.例如:可以实现一个将所有数据类型转换为字符串型的to_string函数代码如下: #include<iostream> template<class

C++ STL 基础及应用(7) 函数对象(仿函数)

把函数作为对象是程序设计的新思维.STL 通过重载类中的 operator() 函数实现函数对象功能,不但可以对容器中的数据进行各种各样的操作,而且能够维护自己的状态.因此,与标准 C 库函数相比,函数对象更为通用. 本章将介绍函数指针的使用.函数对象的定义.引入目的.使用方法,C++98 标准和C++11标准下 STL 内置函数对象的详细介绍.适配器类的使用.包括 bind1st bind2nd not1 not2 mem_fun mem_fun_ref ptr_fun bind ref cr

STL中的各种排序函数

标准c++库中提供六种排序方法:qsort(),  sort(),stable_sort(),  partial_sort(),  list::sort(),  set/multiset qsort()比sort()慢并且不能排序构造函数.虚函数,一般不推荐使用.写一个比较函数传递给qsort()很麻烦: 后五个排序中,前三个是泛型算法,后两个则使用了某些容器的特别特性.所有的这些都是使用operator()来比较对象,但是在必要时指定用户自己的比较函数. 后五个每个都提供了自己的特征: 1.s

STL中各容器之函数总结

一.序列和关联非共有函数 所有标准库共有函数  (构造,相关属性,迭代器,插入与删除,比较,swap) 其中operator>,operator>=,operator<,operator<=,operator==,operator!=均不适用于priority_queue 顺序容器和关联容器共有函数 (1)   序列容器都提供5种相同的构造方法:关联容器都提供3种相同的构造方法 (2)    还有insert插入函数(序列容器有3个相同版本的插入.关联容器也有3个版本的) (3) 

sort在STL库中是排序函数

sort在STL库中是排序函数,有时冒泡.选择等O(N^2)算法会超时时,我们可以使用STL中的快速排序O(N log N)完成排序 sort在<algorithm>库里面,原型如下: 1 2 3 4 template <class RandomAccessIterator>  void sort ( RandomAccessIterator first, RandomAccessIterator last ); template <class RandomAccessIte