为啥更新现有密钥的优先级会产生不一致的结果?

Posted

技术标签:

【中文标题】为啥更新现有密钥的优先级会产生不一致的结果?【英文标题】:Why does updating the priority of existing key yield inconsistent results?为什么更新现有密钥的优先级会产生不一致的结果? 【发布时间】:2021-05-12 09:55:03 【问题描述】:

我正在构建一个具有以下要求的最低优先级队列:

元素具有单独的优先级映射。 可以使用更新的优先级再次插入索引,插入后“旧”索引的位置并不重要。

下面是一个演示意外结果的最小示例。在代码中,我分别将优先级队列123 插入优先级队列102030。然后我将优先级20 -> 5 更改为索引2,并再次插入。

#include <map>
#include <queue>
#include <stdio.h>

int main() 
  std::map<int, int> weight;
  weight[1] = 10;
  weight[2] = 20;
  weight[3] = 30;

  auto compare = [&](int left, int right)  return weight[left] > weight[right]; ;
  std::priority_queue<int, std::vector<int>, decltype(compare)> queue(compare);

  queue.push(1);
  queue.push(2);
  queue.push(3);
  weight[2] = 5;
  queue.push(2);

  while (!queue.empty()) 
    printf("%i ", queue.top());
    queue.pop();
  

运行上面的代码会得到输出1 2 2 3。我希望这会输出2 2 1 3 或者可能是2 1 2 3。奇怪的是,将 weight[2] = 5; 更改为 weight[2] = 35; 会得到预期的输出 1 3 2 2。根据 cppreference,这是作为二进制堆实现的,但我不明白为什么我会得到第一个结果。

【问题讨论】:

因为数据结构不支持。它已经按照自己的顺序保存了数据,并且它不知道您更改了它。您需要删除并重新插入该项目。 '优先级队列不应该知道任何特定的优先级值......它只知道一个是否大于另一个':当然:如果你改变这种关系而不告诉它,你把它弄坏了。就像你在这里一样。 【参考方案1】:

由于先决条件被破坏,程序表现出未定义的行为。

通过更改存储在堆外部的权重,您实际上是在更改堆元素之间的排序关系。

因此,在排序关系发生变化后,已经存储在底层容器中的元素不再构成有效堆

这打破了从那时起几乎所有堆操作的先决条件。

请注意,std::priority_queue 是根据对标准算法 std::make_heapstd::push_heap 等的调用定义的简单包装器。

例如如果我们取priority_queue::push,它会调用push_heap([containers.priqueue.members]/1):

void push(const value_type&amp; x);

效果:好像是:

c.push_back(x);
push_heap(c.begin(), c.end(), comp);

push_heap(见[algorithms.push.heap])有一个前置条件:

要求:[first, last - 1) 范围应为有效堆

【讨论】:

【参考方案2】:

经过一些额外的思考,我相信我明白为什么会发生这种情况。添加较低优先级时,它通过检查所有子级是否大于或等于其父级,因为仅检查添加它的叶子。之后不会执行任何交换并导致“无效”堆。

我制作了一张图片来说明这一点。这里也很容易看出,如果你最初以另一个顺序添加元素,比如132,那么最左边的节点具有优先级30,它将给出输出2 2 1 3

【讨论】:

以上是关于为啥更新现有密钥的优先级会产生不一致的结果?的主要内容,如果未能解决你的问题,请参考以下文章

为啥基于Raft实现的etcd还会出现数据不一致

使用 DateComponent 构造 NSDate 会产生不一致的结果

为啥自动布局不能产生一致的结果?

在自定义 UITableViewCell 中调整 UIImageView 的大小会产生不一致的结果

Spark rdd.count() 产生不一致的结果

eclipse中使用git,代码已经和服务器一致,synchonize视图为啥还会显示图标?如何不显示这些文件?