STL源码分析之大顶堆

来源:互联网 发布:淘宝客新增导购推广 编辑:程序博客网 时间:2024/05/17 20:25

关于大顶堆和小顶堆这里就不再介绍了,这里通过STL再次回顾一下。

heap为了适应容器大小的不断变化,底层调用vector实现

关于heap的算法(这里是大顶堆)

 

push_heap()算法

为了满足完全二叉树的条件,新加入的元素一定是放在最下一层作为叶节点,并填补在由左至右的第一个空格,即插在vector的end()处

 


 

我们通过上溯,将新节点与其父节点进行比较,如果键值比父节点的大,就对换位置,如此一直上溯,直到不需要对换或到了根节点为止。

以下即为push_heap的源码,两个迭代器表示heap底部容器(array或vector)的头尾,并且新的元素已经插入到底部容器的最尾端了。

template <classRandomAccessIterator>inline voidpush_heap(RandomAccessIterator first, RandomAccessIterator last) {  __push_heap_aux(first, last,distance_type(first), value_type(first));} template <class RandomAccessIterator,class Distance, class T>inline void__push_heap_aux(RandomAccessIterator first,                           RandomAccessIterator last, Distance*, T*) {  __push_heap(first, Distance((last - first) -1), Distance(0),              T(*(last - 1)));} //上面为了处理//不管上面的内容,我们只要知道holeIndex就是插入的新节点在array/vector中的下标位置template <classRandomAccessIterator, class Distance, class T>void__push_heap(RandomAccessIterator first, Distance holeIndex,                 Distance topIndex, T value) {  Distance parent = (holeIndex - 1) / 2;                //新插入节点的父节点下标    //循环的一个判断条件就是父节点是存在的 且 父节点的键值小于新节点的键值  while (holeIndex > topIndex && *(first + parent)< value) {    //这里的操作本应该是交换父节点和新节点的位置(或值),因为value被一个变量指向了,所以这里只将新节点的值改为父节点的值,且将要处理的节点改为原来的父节点    *(first + holeIndex) = *(first + parent);holeIndex = parent;//另求当前处理节点的父节点下标    parent = (holeIndex - 1) / 2;  }       //最后将这个指向新址的变量交给当前处理结束的节点  *(first + holeIndex) = value;} 


 

pop_heap()算法

该操作取走根节点,即将整个堆最大的元素取走(其实是将此节点与整个堆的最后一个元素交换位置),然后进行下溯操作,调整整棵树



//这里我们只看核心部分template <classRandomAccessIterator, class T, class Distance>inline void__pop_heap(RandomAccessIterator first, RandomAccessIterator last,                       RandomAccessIterator result, T value,Distance*) {  *result = *first;  __adjust_heap(first, Distance(0),Distance(last - first), value);} template <classRandomAccessIterator, class Distance, class T>void__adjust_heap(RandomAccessIterator first, Distance holeIndex,                   Distance len, T value) {  Distance topIndex = holeIndex;            //当前大顶堆就是从根部开始调整  Distance secondChild = 2 * holeIndex + 2;     //算出第二个儿子的下标位置。为什么要求第二个儿子的下标呢?因为可能这个节点只有1个儿子,可能都没有  while (secondChild < len) {               //右子节点存在    //如果左子节点的键值大于右子节点的键值if (*(first +secondChild) < *(first + (secondChild - 1))) //那么可能要进行交换(有资格去跟父节点键值比较的)的就是左子//因为是完全二叉树,底部容器是vector,所以求左子就是减一操作      secondChild--;    //令当前父节点的值直接等于该大的子节点    //接下来直接修改要调整的节点为该子节点    //原以为会令父子节点进行比较,可没有,是因为这个函数最后又调用了push_heap操作来调整这个节点。。。确实出乎我的意料了    *(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);}



make_heap()算法

即将一个无序的数组转化为heap形式

template <classRandomAccessIterator>inline voidmake_heap(RandomAccessIterator first, RandomAccessIterator last) {  __make_heap(first, last, value_type(first),distance_type(first));} template <class RandomAccessIterator,class Compare, class T, class Distance>void__make_heap(RandomAccessIterator first, RandomAccessIterator last,                 Compare comp, T*, Distance*) {    //0或1个元素就不操作了  if (last - first < 2) return;  Distance len = last - first;    //从有子节点的元素开始做向下调整  Distance parent = (len - 2)/2;     while (true) {    __adjust_heap(first, parent, len, T(*(first+ parent)), comp);if (parent == 0) return;//调整完一个调整继续往前调整,直到根,然后就结束了    parent--;  }}


0 0
原创粉丝点击