[英]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_bound或upper_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.