简体   繁体   English

C++ 优先队列 - 排序间隔

[英]C++ Priority Queue - ordering intervals

Given initially a time interval.最初给定一个时间间隔。 Every time, we pick the largest interval and split that into two halves.每次,我们选择最大的间隔并将其分成两半。 If there is a tie, then we pick the interval with the lowest starting point.如果有平局,那么我们选择起点最低的区间。

for example - [0,9] first split - P1 [0,4] and P2 [4,9]例如 - [0,9] 第一次拆分 - P1 [0,4] 和 P2 [4,9]

For second split: dist(P1) = 3 => if pick P1, new intervals would be [0,2] and [2,4].对于第二次拆分:dist(P1) = 3 => 如果选择 P1,新的间隔将是 [0,2] 和 [2,4]。 dist(P2) = 4 => if pick P2, new intervals are [4, 6] and [6,9] In both the cases, we have to create sub interval of distance 1. So, it's a tie. dist(P2) = 4 => 如果选择 P2,新的区间是 [4, 6] 和 [6,9] 在这两种情况下,我们都必须创建距离为 1 的子区间。所以,这是平局。 and, we pick P1 as P1 < P2.并且,我们选择 P1 为 P1 < P2。

[0,2], [2, 4], [4, 9] [0,2], [2, 4], [4, 9]

Third Split: [0,2], [2, 4], [4,6], [6,9]第三次拆分:[0,2]、[2, 4]、[4,6]、[6,9]

Fourth Split: There is a tie, s0, picked [0,2] [0,1], [1,2], [2,4], [4,6], [6, 9]第四分裂:有平局,s0,选[0,2] [0,1], [1,2], [2,4], [4,6], [6, 9]

Fifth Split: [0,1], [1,2], [2,3], [3,4], [4,6], [6,9]第五分裂:[0,1], [1,2], [2,3], [3,4], [4,6], [6,9]

a possible candidate to be on the top: [4,6]可能的候选人:[4,6]

But, I always get [1,2] on top.但是,我总是把 [1,2] 放在首位。

#include <iostream>
#include <queue>
using namespace std;
int main()
{
    auto dist{ [](const auto & p) {
        return p.second - p.first - 1;
    } };
    auto comp{ 
        [&dist](const auto & p1, const auto & p2) {
            if (abs(dist(p1) - dist(p2)) <= 1) {
                return p1.first > p2.first;
            }
            return dist(p1) < dist(p2);
        }
    };
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(comp)> maxQ{comp};
    maxQ.push({ 0, 9 }); // initial interval
    for (int i{ 0 }; i < 5; ++i) {
        auto ii{ maxQ.top() };
        maxQ.pop();
        int mid = (ii.first + ii.second) / 2;
        maxQ.push({ ii.first, mid });
        maxQ.push({ mid, ii.second });
    }

    while (!maxQ.empty()) {
        auto& ii{ maxQ.top() };
        cout << ii.first << " : " << ii.second << endl;
        maxQ.pop();
    }
}

I'm getting the following output:我得到以下 output:

1 : 2
6 : 9
0 : 1
2 : 3
3 : 4
4 : 6

IMO, 1: 2 interval shouldn't be on top. IMO,1:2 间隔不应该在顶部。 Could someone help me here, why is it so.有人可以在这里帮助我,为什么会这样。

It turns out that this issue has more to do with how priority queues comparators are designed, refer to The reason of using `std::greater` for creating min heap via `priority_queue`事实证明,这个问题更多地与优先级队列比较器的设计方式有关,请参阅The reason of using `std::greater` for create min heap via `priority_queue`

The gist of it is that when two nodes are compared, if the comparator returns true, p1 will fall below p2.它的要点是,当比较两个节点时,如果比较器返回 true,则 p1 将低于 p2。 So a basic ">" comparator, will have smaller nodes at the top, and bigger nodes at the bottom.所以一个基本的“>”比较器,顶部有较小的节点,底部有较大的节点。

To visualize the issue, I ran through it in a debugger, this is the moment at which the (1,2) is being put above (6,9).为了可视化这个问题,我在调试器中运行了它,这是将 (1,2) 置于 (6,9) 之上的时刻。 This is the current state of the priority queue:这是优先级队列的当前 state:

2 : 4
6 : 9
4 : 6
0 : 1
1 : 2

We see that (2,4) is in front (6,9), which is expected since our comparison function says that (2,4) < (6,9) as explained above.我们看到 (2,4) 在前面 (6,9),这是预期的,因为我们的比较 function 说 (2,4) < (6,9) 如上所述。

Then, the code goes to pop the top of the priority queue, meaning replaces (2,4) with the new biggest interval.然后,代码会弹出优先级队列的顶部,这意味着将 (2,4) 替换为新的最大间隔。 How priority queues in C++ do this, is they swap the first element and last elements of the heap, and then reduce the size of it by 1 (so we lose the original first element). C++ 中的优先级队列是如何做到这一点的,它们是交换堆的第一个元素和最后一个元素,然后将其大小减 1(因此我们丢失了原始的第一个元素)。

So after the swap and size reduction, our heap looks like this:所以在交换和减小大小之后,我们的堆看起来像这样:

1 : 2
6 : 9
4 : 6
0 : 1

Then, since the previously deemed smallest element is now at the top of the queue, we need to find its rightful spot.然后,由于之前认为最小的元素现在位于队列的顶部,我们需要找到它的正确位置。

So (1,2) is going to look at its children, (6,9) and (4,6), and see which is more important.所以 (1,2) 将查看它的孩子 (6,9) 和 (4,6),看看哪个更重要。

With our comparison operator, (4,6) is the more important node.对于我们的比较运算符,(4,6) 是更重要的节点。

It then compares, (1,2) with the most important of the previous two nodes, (4,6), to see if it needs to perform a swap to make the queue valid.然后它将 (1,2) 与前两个节点中最重要的节点 (4,6) 进行比较,以查看是否需要执行交换以使队列有效。

It then finds that (1,2) is more important because 1 < 4. Thus, (1,2) stays in its spot and we're left with:然后它发现 (1,2) 更重要,因为 1 < 4。因此,(1,2) 停留在它的位置,我们剩下:

1 : 2
6 : 9
4 : 6
0 : 1

We can plainly see that [1,2] is ordered before [4,6] , by plugging it into your comparator:我们可以清楚地看到[1,2][4,6]之前排序,通过将其插入您的比较器:

comp([1,2], [4,6])
  if (abs(dist([1,2]) - dist([4,6])) <= 1) {   // abs(0 - 1) <= 1   or  1 <= 1
    return 1 > 4;   //  false
  }
  return dist([1,2]) < dist([4,6]);   //  not reached

Only you can correct the comparator to achieve whatever your goal is here, but the existing code is wrong if you want [1,2] to be ordered after [4,6] .只有您可以更正比较器以实现您的目标,但是如果您希望[1,2][4,6]之后排序,则现有代码是错误的。

At a guess, though, based on your description, you might try:不过,根据您的描述,您可以猜测一下:

if (abs(dist(p1)) == abs(dist(p2)))

But I'd go to some lengths to ensure that your ordering is strict weak as it must be for the container.但是我会 go 到一定的长度,以确保您的订购是严格弱的,因为它必须是容器。 Sprinkling some more abs around may help.在周围多撒一些abs可能会有所帮助。

Overall this is quite a complex comparator that's not easy to understand at a glance.总的来说,这是一个相当复杂的比较器,乍一看并不容易理解。

I think It is because the ordering of intervals is not strict.我认为这是因为间隔的顺序不严格。 eg P1(0,1), P2(4,6) and P3(6,9)例如 P1(0,1)、P2(4,6) 和 P3(6,9)

P1 should come before P2. P1 应该在 P2 之前。 P2 should come before P3. P2 应该在 P3 之前。 P3 should come before P1. P3 应该在 P1 之前。

That's crazy.太疯狂了。 How could I set a strict pair ordering here?我怎么能在这里设置严格的配对排序?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM