我有一个表示范围类的对象列表,例如
public class Range
{
int upperValue;
int lowerValue
}
然后我有
List<Range> ranges=new ArrayList<>();
在使用值填充此列表之后或之后,我想检查插入的每个新范围,如果它与列表中的先前值相交。
所以例如,如果我有
[1,4]
[2,4]
[3,5]
我想检测到[3,5]与[1,4]和[2,4]
相交并且[2,4]与[1,4]
相交所以我设法做但但效率不高的是:
首先我使用隔离区按较低的值对列表进行排序。
然后我在排序后再次循环进入列表,并从列表中的第二个元素开始检查它是否与列表中的所有先前元素相交,例如以相反的方式循环。
类似这样的事情
Map<Range,List<Range>> rangeIntersectionMap=new HashMap<>();
for(int i=0;i<ranges.size();i++)
{
if(i>0)
{
List<Range> intersections= rangeIntersectionMap.get(ranges.get(i));
if(intersections==null)
intersections=new ArrayList<>();
for(int j=i-1;j>0;j--)
{
if(checkIntersection(ranges.get(i),ranges.get(j))
{
intersections.add(ranges.get(j));
}
}
rangeIntersectionMap.put(ranges.get(i),intersections);
}
}
这意味着,如果我有20个元素的列表,我有一个因子时间复杂度来检查哪些元素相互交叉。
我认为NavigableMap可能会解决这个问题,但我无法正确使用它。
建议使用其他任何数据结构或算法吗?
答案 0 :(得分:1)
这些问题有Interval Search Tree结构。
这个想法是:
插入:
搜索:
您可以通过Proffesor Sedgewick和Kevin Wayne查看此video和implementation以获取详细说明。
答案 1 :(得分:0)
您可以使用IntervalTree
。
/**
* @param <T> - The type stored in the tree -
* must implement IntervalTree.Interval but beyond that you can do what you like.
* Probably store that value in there too.
* @author OldCurmudgeon
*/
public class IntervalTree<T extends IntervalTree.Interval> {
// My intervals.
private final List<T> intervals;
// My center value. All my intervals contain this center.
private final long center;
// My interval range.
private final long lBound;
private final long uBound;
// My left tree. All intervals that end below my center.
private final IntervalTree<T> left;
// My right tree. All intervals that start above my center.
private final IntervalTree<T> right;
public IntervalTree(List<T> intervals) {
if (intervals == null) {
throw new NullPointerException();
}
// Initially, my root contains all intervals.
this.intervals = intervals;
// Find my center.
center = findCenter();
/*
* Builds lefts out of all intervals that end below my center.
* Builds rights out of all intervals that start above my center.
* What remains contains all the intervals that contain my center.
*/
// Lefts contains all intervals that end below my center point.
final List<T> lefts = new ArrayList<>();
// Rights contains all intervals that start above my center point.
final List<T> rights = new ArrayList<>();
// Track my bounds while distributing.
long uB = Long.MIN_VALUE;
long lB = Long.MAX_VALUE;
for (T i : intervals) {
long start = i.getStart();
long end = i.getEnd();
if (end < center) {
// It ends below me - move it to my left.
lefts.add(i);
} else if (start > center) {
// It starts above me - move it to my right.
rights.add(i);
} else {
// One of mine.
lB = Math.min(lB, start);
uB = Math.max(uB, end);
}
}
// Remove all those not mine.
intervals.removeAll(lefts);
intervals.removeAll(rights);
// Record my bounds.
uBound = uB;
lBound = lB;
// Build the subtrees.
left = lefts.size() > 0 ? new IntervalTree<>(lefts) : null;
right = rights.size() > 0 ? new IntervalTree<>(rights) : null;
/*
* @todo: Build my ascending and descending arrays.
*/
}
/*
* Returns a list of all intervals containing the point.
*/
public List<T> query(long point) {
// Check my range.
if (point >= lBound) {
if (point <= uBound) {
// In my range but remember, there may also be contributors from left or right.
List<T> found = new ArrayList<>();
// Gather all intersecting ones.
// Could be made faster (perhaps) by holding two sorted lists by start and end.
for (T i : intervals) {
if (i.getStart() <= point && point <= i.getEnd()) {
found.add(i);
}
}
// Gather others.
if (point < center && left != null) {
found.addAll(left.query(point));
}
if (point > center && right != null) {
found.addAll(right.query(point));
}
return found;
} else {
// To right.
return right != null ? right.query(point) : Collections.<T>emptyList();
}
} else {
// To left.
return left != null ? left.query(point) : Collections.<T>emptyList();
}
}
private long findCenter() {
//return average();
return median();
}
protected long median() {
// Choose the median of all centers. Could choose just ends etc or anything.
long[] points = new long[intervals.size()];
int x = 0;
for (T i : intervals) {
// Take the mid point.
points[x++] = (i.getStart() + i.getEnd()) / 2;
}
Arrays.sort(points);
return points[points.length / 2];
}
/*
* What an interval looks like.
*/
public interface Interval {
public long getStart();
public long getEnd();
}
/*
* A simple implemementation of an interval.
*/
public static class SimpleInterval implements Interval {
private final long start;
private final long end;
public SimpleInterval(long start, long end) {
this.start = start;
this.end = end;
}
@Override
public long getStart() {
return start;
}
@Override
public long getEnd() {
return end;
}
@Override
public String toString() {
return "{" + start + "," + end + "}";
}
}
public static void main(String[] args) {
// Make some test data.
final int testEntries = 1 * 100;
ArrayList<SimpleInterval> intervals = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < testEntries; i++) {
// Make a random interval.
long start = random.nextLong();
intervals.add(new SimpleInterval(start, start + 1000));
}
ProcessTimer timer = new ProcessTimer();
IntervalTree<SimpleInterval> tree = new IntervalTree<>(intervals);
System.out.println("Took " + timer);
}
}
现在,您可以使用query
方法搜索包含任何特定值的区间。