一系列交叉点算法比O(n)?
-
08-07-2019 - |
题
范围的交叉点是一个简单的,而非微不足道的问题。
它已经回答了两次已经:
第一个方案是O(n)和第二个方案是对一个数据库(这是不到O(n)当然)。
我有同样的问题,但是对于大n和我不在一个数据库。
这个问题似乎是非常相似 存储2D点进行快速检索的这些内部矩形 但我怎么没看到它的地图。
那么什么是数据结构将存储设定的范围,这样一个搜索范围内的费用低于O(n)?(额外的信贷使用的图书馆可用于Java)
编辑:
我想要得到的一个子集的所有交叉的范围,这意味着搜索范围内可以相互交叉的多个范围。
方法需要小于O(n)在Java是:
public class RangeSet {
....
public Set<Range> intersects(Range range);
....
}
其范围仅仅是一类含有对int开始和结束。
这不是不可能的问题,我已经有解决方案,我只是想看看如果有一个更标准的/更简单的方式这样做
解决方案
标准的做法是使用一个 间隔树.
在计算机科学、一间树是树的数据结构持有的时间间隔。具体而言,它允许一个高效率地找到所有的时间间隔重叠的任何给定的时间间隔或点。这是经常使用的窗口查询的,例如,要找到所有道路上的一个计算机化的地图的内部矩视,或找到所有可见光元件的内部三维空间场景.一个类似的数据结构的段树。
琐碎的解决方案是访问的每个区间和测试是否相交给定点或时间间隔,这需要O(n)的时间,其中n为的时间间隔的数目在收集。由于查询可能返回所有的时间间隔,例如,如果查询是一个大型区间的交叉所有的时间间隔在收集,这是渐进的最佳;然而,我们可以做的更好可以通过考虑输出敏感的算法,其中运行时表示,在条款米,间隔生产的查询。间隔树有一个查询的时间O(日志n+m)和一个初创建时的O(n记录n),同时限制存储的消耗O(n)。在创建之后,间隔时间树木可以是动态的,允许有效插入和删除的时间间隔在O(日志n)。如果终点的时间间隔内的一个小整数的范围(例如,在范围[1,...,O(n)]),更快速的数据结构的存在,[1]与预处理时间O(n)和查询的时间O(1+m)为报告米的间隔含有一个给出的查询点。
其他提示
编辑: 这听起来像这样的解决方案或多或少 一间树.一个更完整的执行情况的时间间隔树可以找到 在这里,.
class TreeNode
{
public:
long pivot;
List<Range> leaves; //Any ranges that intersect the pivot
TreeNode left; //Tree nodes that fall to the left of the pivot
TreeNode right; //Tree nodes that fall to the right of the pivot
};
准备O(n记录n):
- 创建该清单的范围
- 挑选的支点(可能通过使用一个排列的结束日期。) ??
- 建立你的树。
搜索:
- 用二进制的搜索,找到第一个枢轴这是>=TestRange.结束
穿越树,直至枢轴>TestRange.开始
2a。添加叶到你的结果。
例如:
范围:
- 0 - 2
- 1 - 2
- 2 - 3
- 1 - 4
- 2 - 4
- 0 - 5
- 4 - 5
- 2 - 6
- 3 - 7
树:
4
--------------+------------------
3 | 7
| 1-4 |
| 2-4 |
| 0-5 |
| 4-5 |
---------+------ --------+--------
2 | null 6 | null
-----+---- 2-3 ----+---- 3-7
null | null null | null
0-2 2-6
1-2
非重叠的范围:
准备O(n记录n):
- 编列/矢量的范围。
- 排序通过结束的范围内(突破的关系通过排序启动的范围)
搜索:
- 用二进制的搜索,找到第一范围内,与一个最终价值的>=TestRange.开始
迭代开始的二进制的搜索直到你找到一个开始>TestRange.结束:
2a。如果范围如果目前的范围内TestRange,将它添加到你的结果。
这取决于你的确切问题,在联的问题的范围不同,没有共同的部分,并将搜查范围可以跨越多个范围。如果你的问题是相同的,它真的很简单:采取一系列的范围,对它们进行排序通过他们的最低值(因为他们没有重叠,这也将相同的顺序排序由他们的上值)。
现在只是做一个binsearch为你的目标更低的数值(或较小,如果不确切)和一个目标上价值(或更大,如果不确切).所得的索引的范围是漫流.你已经检查无论在范围在索引本身是-或排除在外,但是仅有2检查。总体复杂性O(日志n)。
重叠的范围:
准备O(n记录n):
- 编列/矢量的范围。
- 排序通过结束的范围内(突破的关系通过排序启动的范围)
使第二矢量的int.这表明在哪一点你可以停止搜索。
int stop[size]; stop[size-1] = Ranges[size - 1].start; for (int i = size - 2; i >= 0; i--) { stop[i] = min(Ranges[i].start, stop[i+1]); }
搜索:
- 用二进制的搜索,找到第一范围内,与一个最终价值的>=TestRange.开始
迭代开始的二进制的搜索直到停止[i]>TestRange.结束:
2a。如果范围如果目前的范围内TestRange,将它添加到你的结果。
如果范围重叠,和一个想找回 所有 的范围重叠(或包含)一个给定的目标范围内,大多数上述解决方案不会出现工作。
正如一些人指出,如果(最糟糕的情况下) 所有 的范围内发生的交叉目标的范围(例如,如果目标范围是{0..MAXINT}或类似)那当然这需要O(n)返回n范围。
但是不是的有趣的和典型的平均情况下,只有非常小%的n的总范围不相互交叉的目标范围?呼叫的号码 做 相交叉"m"--在这种情况下,你可能能够这样做,以及O(m)。若n=10^9和m=10上,这是一个生死攸关的差异。
考虑简单的情况下,一个文本的文件,具有各种地区的标记于它们的"类型"--也许你想要找到的所有标记的单元包含或相交给定连续的文本范围(例如,第段)。在HTML,XML,或相似的那些只能祖先的文本的节点(s)含有至少一些人物的目标范围内。在典型的陈述与父母指针在每个节点上,这是O(m)--的方式更好的比O(n),特别是因为m(短期或同步目标范围)仅仅是树上筑巢的深度,这往往是甚至低于ln(n),因为大XML文件在实践中得到bushier不深。
有趣的情况下困难:如果你的"要素"不形成一种树作为在XML,但可能重叠,因为在布林克,需要额外的权限,LMNL,以及一些其他系统?你仍然想要找到所有的区域/"要素"重叠的目标,但他们不是那么容易组织。
另一方面,你应该能够做的非常好,因为标记的范围在许多应用程序是最常小的--有更多的词,句和段落的一本书,比有章节。因此,即使有可能是一个数量巨大的范围开始之前的目标和数量庞大,结束后,在交叉点将是非常少于平均水平。
我想这就是原始提问得到了在,我害怕我没有看到一个答案,解决这一问题。如果这不是原来的问题是有关的,那么我想成为一个新的问题。
听起来你需要的一类实现SortedSet接口。TreeSet是执行的船舶的核心。
有一组举行的范围内按最低值,而一个按最高值。
然后可以执行相当于该数据库的算法使用存储集。
至于这是否是实际上的速度比O(n),我不能说。
只是作为一个四树工作组2d点,一个简单的二进制树应该为这种情况。建立一个树与你的范围。
为进一步解释:树上的每个节点包含两个整数,开始和结束的范围,并将两个儿童,如果它不是一片叶子的节点。找到的范围,你输入范围跨度,然后开始在树的顶端
- if the node range intersects the input range:
- if it's a leaf node, then add the range to your result list
- if it's not a leaf node, then traverse down to the child nodes and repeat this process.
它应该是O(的)
进一步的详细说明:二树会结构类似1d版本的四棵树。每一节点将有三个的整数(对不起我说两个以上,但现在我意识到你需要的三),最低的代表的最低值最低的范围内,下面这一节点,最高代表最高价值最高的范围内,下面这一节点,以及枢轴。左边的孩子将跨越这一点的最低于其的枢轴。右边孩子将跨越这一点的枢轴这个节点是最高的。如果只有一个范围从"低"至"最高",你就不会有一个枢轴这将是一片叶子。理想的情况是你想挑转的每个节点,以保持树的平衡。
当我有这个问题,我用一整系列范围和一个二元的搜索,以寻找十字路口。这个就是(我相信)O(日志n)性能,有一点小小的开销,以处理重叠的范围。
回答你的问题是,我认为,可导从码下面,但停止短的插入。我本的整个码,以避免混淆,通过不同的上下文我需要插入一个范围Unicode代码点进入一个名单的代码点的范围。
-编辑--
应下列代码以便确定交叉路口的多个范围涉及一个微不足道的进搜索从插入点直到范围内被发现这不再相交。
-端--编辑
范围类包括:
final int lower; // lower end of range
final int upper; // upper end of range
public int compareTo(Object obj) {
if(obj==null) { return -1; }
Range oth=(Range)obj;
if(lower<oth.lower) { return -1; }
if(lower>oth.lower) { return 1; }
if(upper<oth.upper) { return -1; }
if(upper>oth.upper) { return 1; }
return 0;
}
范围插入:
public Builder addRange(int fir, int las) {
if(fir!=-1) { fir&=0x001FFFFF; }
if(las!=-1) { las&=0x001FFFFF; }
if(codepoints==null || codepoints.length==0) {
codepoints=new Range[]{new Range(fir,las)};
}
else {
int idx=Range.findChar(codepoints,fir);
int ins=(idx<0 ? -(idx+1) : idx);
if(idx<0) {
if (ins>0 && fir==(codepoints[ins-1].upper+1)) { idx=(ins-1); } // new range adjoins the following range (can't overlap or idx would be >=0)
else if(ins<codepoints.length && las>=(codepoints[ins ].lower-1)) { idx=ins; } // new range overlaps or adjoins the following range
}
if(idx<0) {
codepoints=(Range[])Util.arrayInsert(codepoints,ins,new Range(fir,las));
}
else {
boolean rmv=false;
for(int xa=(idx+1); xa<codepoints.length && codepoints[xa].lower<=las; xa++) {
if(las<codepoints[xa].upper) { las=codepoints[xa].upper; }
codepoints[xa]=null;
rmv=true;
}
if(codepoints[idx].lower>fir || codepoints[idx].upper<las) {
codepoints[idx]=new Range((codepoints[idx].lower < fir ? codepoints[idx].lower : fir),(codepoints[idx].upper>las ? codepoints[idx].upper : las));
}
if(rmv) { codepoints=Range.removeNulls(codepoints); }
}
}
return this;
}
二进制的搜索:
static int findChar(Range[] arr, int val) {
if(arr.length==1) {
if (val< arr[0].lower) { return -1; } // value too low
else if(val<=arr[0].upper) { return 0; } // value found
else { return -2; } // value too high
}
else {
int lowidx=0; // low index
int hghidx=(arr.length-1); // high index
int mididx; // middle index
Range midval; // middle value
while(lowidx<=hghidx) {
mididx=((lowidx+hghidx)>>>1);
midval=arr[mididx];
if (val< midval.lower) { hghidx=(mididx-1); } // value too low
else if(val<=midval.upper) { return mididx; } // value found
else { lowidx=(mididx+1); } // value too high
}
return -(lowidx+1); // value not found.
}
}