algorithm - 确定一个值是否在多个范围内的有效方法
问题描述
想象一下,我有一个值:x
和一个值范围:(y to z
假设y < z
)。确定是否x
属于范围y to z
很简单:
if(x >= y && x <=z)
true
else
false
现在想象一下,我再次拥有我的 value x
,但我有一个范围列表:[ y1 to z1
, y2 to z2
, ...]。
请注意,列表中的元素之间没有约束。范围可以重叠。一个范围可以完全落在另一个范围内。等等。
我想回答这个问题是否x
属于这些范围中的任何一个范围?. 当然,我可以使用上述方法遍历列表并检查每个范围,一次一个。但我想知道我是否可以做任何优化。
理想情况下,我希望可能有一个恒定的时间解决方案,也许通过以某种方式对范围进行排序并做一些聪明的事情。但它只是没有来找我。
如果没有恒定时间解决方案,我会接受一个提出优化的答案,并解释为什么这是O(n)
可实现的最佳时间。或者,如果列表中的迭代确实是你能得到的最好的,我也会接受解释为什么会这样的答案。
请注意,我不需要找到所有属于的范围x
。我只需要知道是真是假——至少x
在其中一个范围内。
解决方案
如果您只需要知道查询值是否在某个范围内,并且不需要恢复最小的封闭范围,那么从排序范围列表构建的排序映射就足够了。构建地图将是O(nlogn)
和查询一个点将是O(logn)
。
我们首先对每个范围的开始和结束进行排序,跟踪类型,以便我们可以通过在结束值之前排序开始值来打破平局。然后我们遍历排序列表,当我们遇到一个平衡的结束值时,将一个范围添加到地图中。
下面是一些 Java 代码来说明:
static TreeMap<Integer, Integer> build(int[][] intervals)
{
List<End> ends = new ArrayList<>();
for(int[] i: intervals)
{
ends.add(new End(i[0], Type.START));
ends.add(new End(i[1], Type.END));
}
ends.sort((a, b) -> End.compare(a, b));
int count = 0;
End start = null;
TreeMap<Integer, Integer> imap = new TreeMap<>();
for(End end : ends)
{
if(end.type == Type.START)
{
if(count++ == 0) start = end;
}
else
{
if(--count == 0) imap.put(start.pos, end.pos);
}
}
return imap;
}
实用程序类:
static enum Type {START, END}
static class End
{
int pos;
Type type;
public End(int pos, Type type)
{
this.pos = pos;
this.type = type;
}
public static int compare(End a, End b)
{
int c = Integer.compare(a.pos, b.pos);
if(c == 0) c = a.type.compareTo(b.type);
return c;
}
}
查找就是在映射中找到小于或等于查询的最大键并检查关联值是否大于查询值的简单问题。
static boolean lookup(TreeMap<Integer, Integer> map, int v)
{
Integer k = map.floorKey(v);
if(k != null)
return (v < map.get(k));
return false;
}
测试:
int[][] is = {{1,4}, {4,8}, {10, 12}, {9,14}};
TreeMap<Integer, Integer> imap = build(is);
System.out.printf("Map: %s%n%n", imap);
for(int i=is[0][0]-1;i<is[is.length-1][1]+1; i++)
System.out.println(i + " " + lookup(imap, i));
输出:
Map: {1=8, 9=14}
0 false
1 true
2 true
3 true
4 true
5 true
6 true
7 true
8 false
9 true
10 true
11 true
12 true
13 true
14 false
推荐阅读
- time-complexity - 大 O - Log(A) + Log(B) == Log(AB) 复杂度?
- mongodb - 线程“主”java.lang.NullPointerException 中的异常:无法调用“org.springframework.data.mongodb.core.MongoTemplate.insert(Object)”
- firebase - 搜索栏和列表视图 Flutter
- angular - 重用 trackBy 函数
- css - 将 Primefaces 的样式应用于普通 jsf 组件?
- amazon-web-services - 如何使用放大配置 aws-sdk
- python - 为什么会这样重复?[PYTHON]
- java - 如何从我自己的 Maven 库中修改项目的 Maven 测试执行阶段的命令行参数?
- wordpress - Woocommerce 滚动时在页面上显示绿线
- flutter - Vscode 不显示选项卡输出和问题(仅在调试 Flutter 时)