簡體   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