【问题标题】:Finding max element in min heap在最小堆中查找最大元素
【发布时间】:2020-05-08 13:17:55
【问题描述】:

我有一个最小堆:

std::vector<int> h;
...
std::make_heap(h.begin(), h.end(), std::greater<int>());

最小堆操作std::push_heapstd::pop_heap 在大多数情况下是我需要的,但在极少数情况下我需要找到最小堆中的最大元素。我可以用std::max_element 做到这一点:

std::max_element(h.begin(), h.end());

但是,这必须扫描所有堆元素。

标准库是否提供了一种更有效的算法来查找最小堆中的最大元素?

【问题讨论】:

标签: c++ algorithm optimization stl heap


【解决方案1】:

TL;DR最小堆中,最大元素叶节点X。因此,您可以将搜索限制在堆中大约一半的元素,即,将最大元素的搜索限制在叶节点:

auto max_it = std::max_element(first_leaf_it, h.end());

请注意,这仍然需要线性时间,但常数因子比扫描所有元素要低,大约是一半。


以下是一个类似 STL 的算法实现,用于查找迭代器对提供的最小堆中的最大元素:

template<typename RandomIt>
auto min_heap_max_element(RandomIt first, RandomIt last) {
   auto n = last - first;

   if (n < 2)
      return first;

   auto first_leaf_it = first + (n - 2) / 2 + 1;
   return std::max_element(first_leaf_it, last);
}

将它与您的示例一起使用:

auto max_it = min_heap_max_element(h.begin(), h.end());

如何在堆中找到第一个叶子节点

堆的最后一个元素——h.end()指向的那个——显然是一个叶节点,它的父节点是最后一个非叶节点,因为如果在这个节点之后有一个非叶子节点,我们认为是堆的最后一个元素的元素不会是最后一个元素,这是矛盾的。

因此,第一个叶节点将是紧随最后一个节点的父节点的元素。

你可以很容易地找出最后一个节点的父节点在哪里:给定i堆元素的索引,它的父节点在索引(i - 1) / 2 em> 。所以,最后一个非叶子节点的索引是(h.size() - 2) / 2,因此第一个叶子节点的索引是(h.size() - 2) / 2 + 1


X 假设最小堆中的最大元素位于非叶子节点 取而代之。这意味着它至少有一个子节点。由于min-heap property,此子节点必须大于或等于其父节点。如果子节点大于其父节点(即最大元素),则子节点大于最大值。这是不可能的,因为我们有矛盾。因此,它的所有子节点也必须是最大值,对于这些子节点也是如此。所以,最终,如果最大值在堆中重复,或者唯一的最大值必须对应于一个叶节点,那么最终会有一个最大值位于其中一个叶节点中。

【讨论】:

  • 非常感谢您的解释! :-)
【解决方案2】:

如果您确实需要更好的(即次线性)时间复杂度,请考虑使用Min-max heap。此树中的任何节点都遵循以下属性:

当一个值处于 偶数 级别时,它是其后代中最大的
当一个值处于 奇数 级别时,它是其后代中的最小

因此,根的值最小,而它的两个孩子中的一个具有最大的值。

插入/提取操作的时间复杂度与最小堆相同。

查看Is there a C++ MinMax Heap implementation?

【讨论】:

  • 谢谢我不知道最小-最大堆。
猜你喜欢
  • 2014-05-07
  • 1970-01-01
  • 1970-01-01
  • 2014-07-21
  • 2019-04-11
  • 2010-12-12
  • 2013-12-15
  • 1970-01-01
相关资源
最近更新 更多