压缩间隔列表

时间:2015-10-23 14:58:16

标签: java linked-list iteration

我需要将一个间隔列表压缩成一个较小的列表。让我解释一下:
例如,我有一个包含区间[1,4],[2,5],[5,7],[10,11],[13,20],[19,21]的列表,我想加入相交间隔并返回一个列表[1,7],[10,11],[13,21],将交叉间隔转换为单个较长的间隔。
为此,我写了这个方法:

public List compress(List<Interval> intervals) {

    for (int j = 0; j < intervals.size(); j++) {
        Interval a = intervals.get(j);
        int aIndex = j;

        for (int i = 1 + aIndex; i < intervals.size(); i++) {
            Interval b = intervals.get(i);

            if (a.intersects(b)) {
                //the union method return a union of two intervals. For example returns [1,7] for [1,4] and [2,5]
                intervals.add(j, a.union(b));
                intervals.remove(j+1);
                intervals.remove(i);
            }
        }
    }

    return intervals;
}

这对于检查的第一对间隔似乎工作正常,但它会在那里停止。这是最终输出是包含[1,5],[5,7],[10,11],[13,20],[19,21]的列表。
我发现这可能是非法从列表中删除元素的问题? https://codereview.stackexchange.com/questions/64011/removing-elements-on-a-list-while-iterating-through-it?newreg=cc3f30e670e24cc2b05cd1fa2492906f 但我不知道如何解决这个问题。 请任何人都能给我一个提示。

注意:对不起,如果我做错了,因为这是我的第一篇文章到stackoverflow。并感谢任何想要帮助的人。

更新: 这是我在Maraboc建议创建列表副本并操纵该列表之后找到的解决方案。 这似乎有效。

public List compress(List<Interval> intervals) {

    List<Interval> man = intervals;
    for (int j = 0; j < intervals.size(); j++) {
        Interval a = intervals.get(j);
        int aIndex = j;

        for (int i = 1 + aIndex; i < intervals.size(); i++) {
            Interval b = intervals.get(i);

            if (a.intersects(b)) {
                a = a.union(b);
                man.add(j,a);
                man.remove(j+1);
                man.remove(i);
                i--;
            }
        }
    }

    return intervals;
}

谢谢大家。

5 个答案:

答案 0 :(得分:1)

你实际上没有使用迭代器,你正在使用for-cycles并根据它们的位置从列表中选择元素,因此你不必害怕&#34;我无法在迭代时删除&#34;问题。

答案 1 :(得分:1)

我首先将这个问题发布到了stackexchange上。他们把我改到了这个地方,问题被搁置了。但在此之前发生了Maraboc [链接](https://codereview.stackexchange.com/users/87685/maraboc ) 帮助一个想法。他告诉我要创建一个新列表并修改它。我做到了,它似乎工作。更新的解决方案将在更新的问题中。

答案 2 :(得分:1)

为了好玩,我采用了现有的Interval Tree并添加了一个似乎运行良好的minimise方法。

/**
 * Title: IntervlTree
 *
 * Description: Implements a static Interval Tree. i.e. adding and removal are not possible.
 *
 * This implementation uses longs to bound the intervals but could just as easily use doubles or any other linear value.
 *
 * @author OldCurmudgeon
 * @version 1.0
 * @param <T> - The Intervals to work with.
 */
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<>();

        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) {
                lefts.add(i);
            } else if (start > center) {
                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);
        uBound = uB;
        lBound = lB;

        // Build the subtrees.
        left = lefts.size() > 0 ? new IntervalTree<>(lefts) : null;
        right = rights.size() > 0 ? new IntervalTree<>(rights) : null;

        // Build my ascending and descending arrays.
        /**
         * @todo Build my ascending and descending arrays.
         */
    }

    /*
     * Returns a list of all intervals containing the point.
     */
    List<T> query(long point) {
        // Check my range.
        if (point >= lBound) {
            if (point <= uBound) {
                // Gather all intersecting ones.
                List<T> found = intervals
                        .stream()
                        .filter((i) -> (i.getStart() <= point && point <= i.getEnd()))
                        .collect(Collectors.toList());

                // 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();
        }

    }

    /**
     * Blends the two lists together.
     *
     * If the ends touch then make them one.
     *
     * @param a
     * @param b
     * @return
     */
    static List<Interval> blend(List<Interval> a, List<Interval> b) {
        // Either empty - lreturn the other.
        if (a.isEmpty()) {
            return b;
        }
        if (b.isEmpty()) {
            return a;
        }
        Interval aEnd = a.get(a.size() - 1);
        Interval bStart = b.get(0);
        ArrayList<Interval> blended = new ArrayList<>();
        // Do they meet?
        if (aEnd.getEnd() >= bStart.getStart() - 1) {
            // Yes! merge them.
            // Remove the last.
            blended.addAll(a.subList(0, a.size() - 1));
            // Add a combined one.
            blended.add(new SimpleInterval(aEnd.getStart(), bStart.getEnd()));
            // Add all but the first.
            blended.addAll(b.subList(1, b.size()));
        } else {
            // Just join them.
            blended.addAll(a);
            blended.addAll(b);
        }
        return blended;
    }

    static List<Interval> blend(List<Interval> a, List<Interval> b, List<Interval>... more) {
        List<Interval> blended = blend(a, b);
        for (List<Interval> l : more) {
            blended = blend(blended, l);
        }
        return blended;
    }

    List<Interval> minimise() {
        // Calculate min of left and right.
        List<Interval> minLeft = left != null ? left.minimise() : Collections.EMPTY_LIST;
        List<Interval> minRight = right != null ? right.minimise() : Collections.EMPTY_LIST;
        // My contribution.
        long meLeft = minLeft.isEmpty() ? lBound : Math.max(lBound, minLeft.get(minLeft.size() - 1).getEnd());
        long meRight = minRight.isEmpty() ? uBound : Math.min(uBound, minRight.get(0).getEnd());
        return blend(minLeft,
                Collections.singletonList(new SimpleInterval(meLeft, meRight)),
                minRight);
    }

    private long findCenter() {
        //return average();
        return median();
    }

    protected long median() {
        if (intervals.isEmpty()) {
            return 0;
        }
        // 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 + "}";
        }
    }

    /**
     * Test code.
     *
     * @param args
     */
    public static void main(String[] args) {
        /**
         * @todo Needs MUCH more rigorous testing.
         */
        // Test data.
        long[][] data = {
            {1, 4}, {2, 5}, {5, 7}, {10, 11}, {13, 20}, {19, 21},};
        List<Interval> intervals = new ArrayList<>();
        for (long[] pair : data) {
            intervals.add(new SimpleInterval(pair[0], pair[1]));
        }
        // Build it.
        IntervalTree<Interval> test = new IntervalTree<>(intervals);

        // Check minimise.
        List<Interval> min = test.minimise();
        System.out.println("Minimise test: ---");
        System.out.println(min);
    }
}

答案 3 :(得分:0)

要使算法正常工作,必须对间隔进行排序,例如,开始。

然后for-i循环可以使a成为最长的间隔。

       if (a.intersects(b)) {
            a = a.union(b);
            intervals.remove(i);
            --i; // So we remain at old i value.
        }
    } // for i
    intervals.set(j, a);

这些要求的原因是间隔A,B,C可能形成一个长间隔ABC,而C. B,A可能。

答案 4 :(得分:0)

确实问题是当你从列表中删除一个元素时,所有后续元素都会被移位。大约j我估计它没有改变,因为你插入然后在同一位置删除一个项目。但是,位置i的移除将移动列表中的所有元素。

您可以做的不是删除元素,而是在该位置放置一个空值,以使索引保持不变。然后,您必须执行最后一次传递以从数组中删除空元素(并在比较之前检查空值)。

您还可以向后运行内部循环(从最大i向下运行到j),以便在i之后已经处理过的任何元素都已被处理。