Java检查列表

时间:2017-04-20 11:27:31

标签: java performance data-structures range

我有一个表示范围类的对象列表,例如

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可能会解决这个问题,但我无法正确使用它。

建议使用其他任何数据结构或算法吗?

2 个答案:

答案 0 :(得分:1)

这些问题有Interval Search Tree结构。

这个想法是:

  • 每个节点代表间隔,保存节点中的两个端点
  • 在每个节点中保存其子树的最大(右)端点值
  • 搜索键是两个
  • 的较小值

插入:

  • 使用低值导航插入
  • 相应地更新子树中的最大值

搜索:

  • 如果节点中的间隔与查询间隔相交,则将其返回
  • 如果left subtree为null,则返回
  • 否则如果左子树中的最大值小于搜索到的低值,则右键
  • 别走了

您可以通过Proffesor Sedgewick和Kevin Wayne查看此videoimplementation以获取详细说明。

答案 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方法搜索包含任何特定值的区间。