为啥更新现有密钥的优先级会产生不一致的结果?
Posted
技术标签:
【中文标题】为啥更新现有密钥的优先级会产生不一致的结果?【英文标题】:Why does updating the priority of existing key yield inconsistent results?为什么更新现有密钥的优先级会产生不一致的结果? 【发布时间】:2021-05-12 09:55:03 【问题描述】:我正在构建一个具有以下要求的最低优先级队列:
元素具有单独的优先级映射。 可以使用更新的优先级再次插入索引,插入后“旧”索引的位置并不重要。下面是一个演示意外结果的最小示例。在代码中,我分别将优先级队列1
、2
和3
插入优先级队列10
、20
和30
。然后我将优先级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_heap
、std::push_heap
等的调用定义的简单包装器。
例如如果我们取priority_queue::push
,它会调用push_heap
([containers.priqueue.members]/1):
void push(const value_type& x);
效果:好像是:
c.push_back(x); push_heap(c.begin(), c.end(), comp);
而push_heap
(见[algorithms.push.heap])有一个前置条件:
要求:
[first, last - 1)
范围应为有效堆。
【讨论】:
【参考方案2】:经过一些额外的思考,我相信我明白为什么会发生这种情况。添加较低优先级时,它通过检查所有子级是否大于或等于其父级,因为仅检查添加它的叶子。之后不会执行任何交换并导致“无效”堆。
我制作了一张图片来说明这一点。这里也很容易看出,如果你最初以另一个顺序添加元素,比如1
、3
、2
,那么最左边的节点具有优先级30
,它将给出输出2 2 1 3
。
【讨论】:
以上是关于为啥更新现有密钥的优先级会产生不一致的结果?的主要内容,如果未能解决你的问题,请参考以下文章
使用 DateComponent 构造 NSDate 会产生不一致的结果