make_heap()函数的工作原理
How make_heap() function works?
我对矢量和迭代器有基本的了解。但我在理解以下代码片段的输出时遇到了问题。
具体来说,我无法找到make_heap()函数的功能。产量:91 67 41 24 59 32 23 13!!
据我所知,堆将如下所示:
91
/
67 41
/ /
59 24 32 23
/
13
因此,我期望输出为:91 67 41 59 24 32 23 13
如果有人能帮助我理解make_heap()是如何生成这样的输出的,我将不胜感激。
int main()
{
int aI[] = { 13, 67, 32, 24, 59, 41, 23, 91 };
vector<int> v(aI, aI + 8);
make_heap( v.begin(), v.end() );
vector<int>::iterator it;
for( it = v.begin(); it != v.end(); ++it )
cout << *it << " ";
//Output: 91 67 41 24 59 32 23 13
return 0;
}
二进制堆必须满足两个约束(除了作为二进制树之外):
- shape属性-树是一个完整的二叉树(除了最后一级)
- heap属性:每个节点都大于或等于其每个子节点
二进制堆中兄弟节点的顺序不是由堆属性指定的,并且单个节点的两个子节点可以自由交换,除非这样做违反了shape属性。
因此,在您的示例中,您可以在第二级的节点之间自由交换,并获得多个合法的输出。
当堆积未排序的数组时,该算法利用了这样一个特点,即数组的一半将是叶节点(数组中较高的索引),另一半将是这些叶节点的父节点。该算法只需要迭代父节点并修复它们的逻辑子树。叶节点一开始是有效的子堆,因为根据定义,它们大于不存在的子节点。
因此,我们只需要修复至少有一个非叶节点的子堆。按照正确的顺序(从数组的中间到最低索引)完成,当最后一个父节点被堆积时,整个数组将是一个有效的堆。
每个步骤看起来如下:
iteration 1:
13 67 32 24 59 41 23 91
^ current parent under consideration
^ children of this parent
13 67 32 91 59 41 23 24 after heapifying this sub-tree
-- --
iteration 2:
13 67 32 91 59 41 23 24
^ current parent under consideration
^ ^ children of this parent
13 67 41 91 59 32 23 24 after heapifying this sub-tree
-- --
iteration 3:
13 67 41 91 59 32 23 24
^ current parent under consideration
^ ^ children of this parent
13 91 41 67 59 32 23 24 after heapifying this sub-tree
-- --
iteration 4:
13 91 41 67 59 32 23 24
^ current parent under consideration
^ ^ children of this parent
91 13 41 67 59 32 23 24 heapify swap 1
-- --
91 67 41 13 59 32 23 24 heapify swap 2
-- --
91 67 41 24 59 32 23 13 after heapifying this sub-tree
-- --
堆化数组的简单方法是遍历从索引0
到n-1
的数组,并在每次迭代时将该索引处的元素"添加"到由该索引之前的元素组成的堆中。这将产生您所期望的堆。该算法产生了n
堆运算。make_heap()
使用的算法产生了n/2
的堆运算。它会产生一个不同但仍然有效的堆。
make_heap
通过对元素进行重新排序,使它们满足堆约束,在向量中构造一个二进制堆。构建的堆是最大堆,也就是说,它将的最大(根据operator<
或提供的compare)元素放在第一个元素中,即堆的根,它是向量的第一个元素。
二进制堆是一个平衡的二叉树,满足父节点中的值总是大于子节点的值(在这种情况下,越小越常见)的条件。这意味着根总是包含最大的元素。结合有效的根提取,这就形成了一个很好的优先级队列。
二进制堆以广度优先的预序存储在数组中。也就是说,根在位置0,它在位置1和2的直接子级,在位置3和4的1的子级,以及在位置5和6的2的子级等等。通常,节点n
的子级在位置2*n + 1
和2*n + 2
。
在C++中,make_heap
函数与push_heap
和pop_heap
一起实现了一个完全优先于向量的队列。还有一个priority_queue
容器包装器将它们组合在一个类中。
优先级队列主要用于著名的Dijkstra算法和各种调度算法。因为Dijkstra的算法需要选择最小值,所以更常见的是在根中定义具有最小值的堆。C++标准库选择用最大值来定义它,但请注意,您可以通过给它greater_than
而不是less_than
作为比较器来获得最小堆。
构建堆有两种方法。通过将每个元素推到它上面,或者通过固定元素的前半部分的不变量(后半部分是叶子)。后者效率更高,因此:
- [13,67,32,24,59,41,23,91]
- 24<91
- [13,67,32,91,59,41,23、24]
- 32<41
- [13,67,41,91,59、32,23,24]
- 67<91
- [13,91,41、67、59、32、23、24]
- 13<91
- [91,13,41,67,59、32,23,24]
- 向下移动的元素可能仍然违反约束,并且这一次它确实违反了:13<67
- [91,67,41,13,59,32,23,24]
- 并且仍然违反约束:13<24
- [91,67,41,24,59,32,23,13]
- 根处理后,我们就完成了
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++中的memset函数工作不正常
- 将返回值存储在函数指针数组的指针中是如何工作的?
- insertElement() 函数无法按预期工作
- 即使没有调用这个递归函数,它是如何工作的?
- type_info成员函数如何工作?
- 如何定义在用作函数参数时工作的类模板的转换
- 优先级队列构造函数的工作
- 有人可以解释一下这个矩阵幂函数是如何工作的吗?
- 使用移动和复制语义时函数匹配如何工作?
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- C++代码停止工作错误使用cout内部函数
- 好友函数的工作原理
- 为什么 sizeof(ar)/ sizeof(ar[0]) 在传递给函数时无法在向量上正常工作?
- 以下代码如何工作以每次为唯一调用堆栈唯一实例化模板函数?
- 递归函数调用在后台工作
- 如何使函数在派生类中工作?
- 在下面的代码中,虚函数是如何工作的
- 工作函数重载如何使用可变参数解析?
- C++继承和具有虚拟工作函数的Pthreads