簡體   English   中英

比 O(N) 更好的搜索排序區間向量的解決方案

[英]A better than O(N) solution for searching a vector of sorted intervals

給定一組排序區間(first >= second),按區間的第一個元素排序:

{1, 3}, {1, 2}, {2, 4}, {2, 2}, {2, 3}, {3, 5}, {3, 3}, {3, 7}

是否有一種有效的算法來確定與給定輸入間隔相交的第一個間隔? 例如:

Query ({0, 0}) = returns end()
Query ({2, 4}) = returns iterator to element 0
Query ({3, 8}) = returns iterator to element 0
Query ({4, 9}) = returns iterator to element 2
Query ({7, 8}) = returns iterator to element 7
Query ({8, 9}) = returns end()

高效我的意思是比 O(N) 更好。 我有一種模糊的感覺,這個問題有一個lower_boundupper_bound的解決方案,但我沒有足夠的精神力量來解決這個問題。

這是我不滿意的 O(N) 解決方案。

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

int main()
{
    using Interval = std::pair<int, int>;
    using Sequence = std::vector<Interval>;
    using Iterator = Sequence::const_iterator;

    auto Query = [](Sequence const & sequence, Interval const & interval) -> Iterator
    {
        return std::find_if(sequence.begin(), sequence.end(), [interval](Interval const & other) {
            return interval.first <= other.second && interval.second >= other.first;
        });
    };

    auto Print = [](Sequence const & sequence, Iterator const & iterator) -> void
    {
        if (iterator == sequence.cend())
        {
            std::cout << "end()\n";
        }
        else
        {
            std::cout << std::to_string(std::distance(sequence.cbegin(), iterator)) << "\n";
        }
    };

    Sequence sequence = {
        {1, 3}, { 1, 2 }, { 2, 4 }, { 2, 2 }, { 2, 3 }, { 3, 5 }, { 3, 3 }, { 3, 7 }
    };

    auto result = Iterator();

    result = Query(sequence, { 0, 0 });

    Print(sequence, result);

    result = Query(sequence, { 2, 4 });

    Print(sequence, result);

    result = Query(sequence, { 3, 8 });

    Print(sequence, result);

    result = Query(sequence, { 4, 9 });

    Print(sequence, result);

    result = Query(sequence, { 7, 8 });

    Print(sequence, result);

    result = Query(sequence, { 8, 9 });

    Print(sequence, result);
}

Output:

end()
0
0
2
7
end()

不可能有比線性算法更快的算法。 考慮輸入,其中每個元素的第一個值為 0,第二個值在某個范圍內是隨機均勻的。 將查詢視為{x,x+1} ,其中x在同一范圍內。

由於您想要“與給定輸入相交的第一個間隔”,因此排序現在沒用了。 您必須全部掃描。

因此,您無法擊敗O(N)

正如評論中所說,使用問題中概述的數據結構,你不能做得比線性更好

您不能跳過任何左邊界低於請求區間右邊界的區間,因為右邊界(沒有排序/約束)總是可以假設一個可能使檢查的區間相交的值,所以你不能使用二分搜索立即跳轉到“有趣的區域”。

盡管一旦到達左邊界大於查詢右邊界的區間,您就可以提前退出。

但是,鑒於 OP 可以更改數據表示形式,一旦您使間隔不重疊,這將變得更加容易; 鑒於我們只對相交/不相交測試感興趣,這不是什么大問題。

從排序的數據開始,合並重疊間隔只是一次線性傳遞的問題:保持“當前”元素,並在讀取重疊元素時保持增長; 一旦你得到一個不相交的,推動“當前”的,並使其成為新的“當前”。 這也可以很容易地在原地工作,作為通常的remove_if算法的變體。

對不相交的間隔進行排序后,您可以將二進制搜索應用於您的問題以進行 O(log n) 查詢。 代替vector<pair>的更有利的結構是普通的vector<int> ,其(排序的)邊界被展平為單個序列。

使用這種表示,查詢如下所示:

  • 做一個upper_bound尋找查詢間隔的左邊界;
  • 如果你得到end ,那么沒有界限更大,所以我們沒有交集;
  • 否則,從迭代器獲取索引:
    • 如果索引為奇數,則表示大於搜索值的第一個邊界是右邊界; 這意味着查詢左邊界落在一個區間內,因此我們有一個交集;
    • 否則,查詢的左邊界落在兩個區間之間的一個洞里,所以我們必須檢查查詢的右邊界; upper_bound找到的(偶數索引)元素是下一個區間的左邊界:如果它小於查詢的右邊界,則我們有一個交集,否則查詢區間完全落在洞內,所以有沒有交叉點。

對於給定的輸入,無法檢查每個間隔(最壞情況)。

但是,如果您有很多查詢,您可以一次處理輸入以使查詢更快。

首先創建

struct Interval {
    int start, end;
    size_t first_index;
};
std::vector<Interval> intervals;

然后從輸入間隔中填充它,修改每個間隔以便沒有重疊。

假設您有(1, 4)(3, 6)然后首先添加{1, 4, 0} ,然后添加{4, 6, 1} 間隔重疊的部分(3, 4)查詢需要返回第一個間隔,以便您從第二個間隔中剪掉該部分。

此過程需要O(n)時間,但之后您可以使用二進制搜索來查找間隔。 因此,它只會在許多查詢中更快地攤銷。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM