我在查找两个列表中的所有重叠范围时遇到问题 此问题与This问题类似,但输入不同。
我有2个输入文件,一个包含多行范围和数据对,另一个包含用于查找交叉点的范围列表。
我已经编写了一个文件阅读器类,它从数据文件中读取,一次返回一个对象,其中包含范围和数据对的列表,但是当我试图找到两者的重叠时遇到了麻烦范围列表。
目前我正在做的是强制它,将数据列表中的每个范围与交叉点列表中的每个其他范围进行比较,但由于数据文件非常大,所以需要很长时间时间。
示例对象:
这是数据列表中的对象:
public DataModel {
private int start; {set; get;}
private int end; {set; get;}
//Other Data
}
范围模型只是成对整数的列表(开始,结束)。
while (fileParser.hasNext()) {
dataList = fileParser.next();
for (DataModel data : dataList)
for (RangeModel range : rangeList)
if(overlaps(data, range))
print(range.getString + " " + data.getString);
}
为清晰起见编辑:
DataModel以较小长度的相似范围的较小数据包给出,但它们大多在20以下,因此比较将在相同的RangeModel和每个新的DataModel上重复运行。 所有数据的总范围约为20亿,但这并不重要。谢谢你的帮助。
答案 0 :(得分:1)
我可以考虑不同的优化,但它们取决于检查后您想要的数据类型。
对数据和范围进行排序并按顺序处理它们可以立即提高性能,因为测试从100开始的范围与以50结尾的另一个范围是没有意义的。
另一个改进是“压缩”范围。如果您有(1-10),(10-20),(20-30)等范围,那么您可以轻松地用单个(1-30)范围替换它们,并减少测试次数。您可以创建一个适当的AggregateRange类,用于跟踪其组成范围的标识,以防您仍然想知道哪个原始范围导致重叠。
另一项改进是在处理数据列表时巧妙地使用以前的结果。例如:假设您测试数据范围(1-10)并且它恰好不重叠。如果下一个测试数据范围为(2-8),则不需要针对范围进行测试,因为您之前的结果保证它不会重叠。
这种改进背后的基本思想是推进任何未经测试的数据范围的开始,直至并包括最后一个非重叠数据范围的结束。如果新的开始超过其自己的结束,则不需要测试,因为它不重叠。 这意味着非重叠(1-20)应该将未经测试的(10-100)转换为未经测试的(20-100)。这可能比较难实现,所以要小心不要过度。
答案 1 :(得分:1)
检查我的理解是否正确:
DataModel
s,以及少量RangeModel
s。 (尽管我的解决方案没有利用这种不对称性)我要描述的方法可以在2个范围列表之间进行范围交叉,无论范围如何(重叠,大范围等)。限制是2个范围列表的大小之和(排序是瓶颈),以及找到的范围数(迭代是瓶颈)。
将范围拆分为2个EndPoint
个对象,表示:值(int
),范围的开始或结束(boolean
),起始EndPoint
对象(范围开始时的null
;指向范围开始的EndPoint
对象(如果范围结束),标记(int
,标记是数据还是范围查询)。
将两个范围列表中的所有EndPoint
放在一起,按值对它们进行排序,通过将start放在结束端点的前面来打破中断(如果您考虑触摸交叉点)。排序步骤的复杂性是O((m + n)log(m + n))。
根据此伪代码循环遍历排序的EndPoint
:
open_data = HashSet()
open_range = HashSet()
for e in endpoints:
if e is start of range:
if e is data:
print e intersect with all in open_range
open_data.add(e)
else: // e is range to test
print e intersect with all in open_data
open_range.add(e)
else: // e is end of range
if e is data:
open_data.remove(e.startPoint)
else: // e is range to test
open_range.remove(e.startPoint)
从HashSet中添加和删除是分摊O(1)。问题在于打印交叉点,即O(k),其中k是交叉点的数量,在最坏的情况下可以达到O(m * n)。
组合,在最坏的情况下复杂度为O((m + n)log(m + n)+ m * n)。您可以根据数据的属性做得更好。这是一个非常通用的解决方案。
答案 2 :(得分:0)
理想的解决方案取决于数据的具体特征,但排序两个输入集将是一个很好的第一步,这将使您减少所需的比较量。
一个选项是从min(startTime)到max(endTime)创建一个数组,并在每个位置存储对覆盖此范围的输入值的引用。
因此,如果您输入的是 答:[1-5]和B:[3-7],你可以有一个看起来像
的数据结构1: A
2: A
3: A,B
4: A,B
5: A,B
6: B
7: B
然后,为了测试[2-4]与数据集的交集,您只需在数组列表中查找2,3,4并连接结果。
如果您只关心IF是否有交叉点,而不是交叉口的确切位置,则可以进一步提高速度。或者,如果您只关心AN交叉点,而不是所有交叉点。
答案 3 :(得分:0)
您可以对每个范围列表o(N ln N)进行排序,并对这些范围进行合并排序O(N)
这将显示任何重叠范围,并且CPU时间最短。