繁体   English   中英

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

[英]C++ Priority Queue - ordering intervals

最初给定一个时间间隔。 每次,我们选择最大的间隔并将其分成两半。 如果有平局,那么我们选择起点最低的区间。

例如 - [0,9] 第一次拆分 - P1 [0,4] 和 P2 [4,9]

对于第二次拆分:dist(P1) = 3 => 如果选择 P1,新的间隔将是 [0,2] 和 [2,4]。 dist(P2) = 4 => 如果选择 P2,新的区间是 [4, 6] 和 [6,9] 在这两种情况下,我们都必须创建距离为 1 的子区间。所以,这是平局。 并且,我们选择 P1 为 P1 < P2。

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

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

第四分裂:有平局,s0,选[0,2] [0,1], [1,2], [2,4], [4,6], [6, 9]

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

可能的候选人:[4,6]

但是,我总是把 [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();
    }
}

我得到以下 output:

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

IMO,1:2 间隔不应该在顶部。 有人可以在这里帮助我,为什么会这样。

事实证明,这个问题更多地与优先级队列比较器的设计方式有关,请参阅The reason of using `std::greater` for create min heap via `priority_queue`

它的要点是,当比较两个节点时,如果比较器返回 true,则 p1 将低于 p2。 所以一个基本的“>”比较器,顶部有较小的节点,底部有较大的节点。

为了可视化这个问题,我在调试器中运行了它,这是将 (1,2) 置于 (6,9) 之上的时刻。 这是优先级队列的当前 state:

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

我们看到 (2,4) 在前面 (6,9),这是预期的,因为我们的比较 function 说 (2,4) < (6,9) 如上所述。

然后,代码会弹出优先级队列的顶部,这意味着将 (2,4) 替换为新的最大间隔。 C++ 中的优先级队列是如何做到这一点的,它们是交换堆的第一个元素和最后一个元素,然后将其大小减 1(因此我们丢失了原始的第一个元素)。

所以在交换和减小大小之后,我们的堆看起来像这样:

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

然后,由于之前认为最小的元素现在位于队列的顶部,我们需要找到它的正确位置。

所以 (1,2) 将查看它的孩子 (6,9) 和 (4,6),看看哪个更重要。

对于我们的比较运算符,(4,6) 是更重要的节点。

然后它将 (1,2) 与前两个节点中最重要的节点 (4,6) 进行比较,以查看是否需要执行交换以使队列有效。

然后它发现 (1,2) 更重要,因为 1 < 4。因此,(1,2) 停留在它的位置,我们剩下:

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

我们可以清楚地看到[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

只有您可以更正比较器以实现您的目标,但是如果您希望[1,2][4,6]之后排序,则现有代码是错误的。

不过,根据您的描述,您可以猜测一下:

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

但是我会 go 到一定的长度,以确保您的订购是严格弱的,因为它必须是容器。 在周围多撒一些abs可能会有所帮助。

总的来说,这是一个相当复杂的比较器,乍一看并不容易理解。

我认为这是因为间隔的顺序不严格。 例如 P1(0,1)、P2(4,6) 和 P3(6,9)

P1 应该在 P2 之前。 P2 应该在 P3 之前。 P3 应该在 P1 之前。

太疯狂了。 我怎么能在这里设置严格的配对排序?

暂无
暂无

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

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