【问题标题】:Intersecting rectangles相交的矩形
【发布时间】:2010-07-14 06:04:17
【问题描述】:

这是一个解析几何类型的问题。我不确定我可以在这里发布它。但是我必须想出一个 Java 函数来执行此功能。我在页面/摆动容器中有多个矩形。我知道矩形的边界。现在我需要找到哪些矩形彼此相交。这里有一个好处,相交的矩形将始终具有相同的 y 分量,并且所有矩形的高度相同。我必须根据它们的 x 坐标对矩形进行配对和宽度

例如

Rect1 has bounds:20,10,50,20
Rect2 has bounds:60,10,30,20
Rect3 has bounds:40,10,40,20
Rect4 has bounds 20,30,40,20
Rect5 has bounds 20,50,30,20

现在我的方法应该返回

Rect1 and Rect2
Rect2 and Rect3
Rect3 and Rect1

有没有什么算法或者有没有人尝试过?给你的建议

编辑:更具体地说,我的矩形实际上是一个 JLabel。我将标签放置在表格的行内。

【问题讨论】:

    标签: java algorithm swing


    【解决方案1】:

    1) 首先,我同意其他人指出这实际上是一个一维问题:给定一组段,找出所有相交的对。

    2) 请注意,在最坏的情况下,您不能保证比 O(N^2) 更好,因为这些段可能都相互重叠。

    3) 假设矩形的数量很大,并且交叉点的数量并不总是 N 的三次方,我会使用扫描技术:

    A) 按升序对所有段的起点和终点进行排序。

    B) 遍历列表,收集途中的交叉点。每次迭代代表正在扫描的轴的一部分,其中覆盖它的段很容易确定。

    4) 请注意,如果您只需要 number 个交叉点,则可以在 O(N log N) 时间内完成。

    这是一个通用实用程序,可以有效地完成这项工作。在底部,您可以找到一个使用示例。请记住,此解决方案仅在您不期望有很多交叉路口时才有意义。此外,对于少数部分来说,这是一种过度杀伤(我想这是你的情况 - 因为你正在使用 N

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.List;
    import java.util.AbstractMap.SimpleEntry;
    
    
    public class SegmentSet <T> {   
        private List<Segment> segments = new ArrayList<Segment>();  
    
        //note that x2 is inclusive
        public void add(int x1, int x2, T identity) {
            segments.add(new Segment(x1,x2, identity));
        }
    
        public List<SimpleEntry<T, T>> getAllIntersectingPairs() {
            // Build a list of all segment edges
            ArrayList<Edge> edges = new ArrayList<Edge>(2 * segments.size());
            int i=0;
            for(Segment seg : segments) {
                edges.add(new Edge(EdgeType.START, seg.x1, seg));
                edges.add(new Edge(EdgeType.END, seg.x2, seg));
            }
    
            // Sort the edges in ascending order
            Collections.sort(edges);
    
            // Sweep
            ArrayList<SimpleEntry<T, T>> res = new ArrayList<SimpleEntry<T, T>>();
            HashMap<Segment, Object> currSegments = new HashMap<Segment, Object>();
            for (Edge edge : edges) {
                if (edge.type == EdgeType.START) {
                    for (Segment seg : currSegments.keySet())
                        res.add(new SimpleEntry<T, T>(edge.seg.identity, seg.identity));
                    currSegments.put(edge.seg, null);
                } else {
                    currSegments.remove(edge.seg);
                }
            }
    
            return res;
        }
    
        public class Segment {
            public final int x1;
            public final int x2;
            public final T identity;
    
            public Segment(int x1, int x2, T identity) {
                this.x1 = x1;
                this.x2 = x2;
                this.identity = identity;
            }
        }
    
        private enum EdgeType {START, END};
    
        private class Edge implements Comparable<Edge>{
            public final EdgeType type;
            public final int x;
            public Segment seg;
    
            public Edge(EdgeType type, int x, Segment seg) {
                this.type = type;
                this.x = x;
                this.seg = seg;
            }
    
            @Override
            public int compareTo(Edge o) {
                if (x > o.x)
                    return 1;
                if (x < o.x)
                    return -1;
                // A start Edge will come before an end edge in case of equal X value
                return type.ordinal() - o.type.ordinal();
            }
        }
    
        public static void main(String[] args) {
            SegmentSet<String> set = new SegmentSet<String>();
            set.add(10,100,"A");
            set.add(110,200,"B");
            set.add(0,400,"C");
            System.out.println(set.getAllIntersectingPairs());
        }
    }
    

    【讨论】:

    • 感谢您的宝贵时间。非常感谢您。我需要将与其他标签相交的矩形(标签)的高度设为原来的一半。更复杂的情况是当有 n 个交叉点时,我必须减少 n。但是现在 2 已经绰绰有余了。
    【解决方案2】:

    如果相交的矩形总是具有相同的 y 坐标,这实际上不是二维重叠问题。这是一组一维重叠检查,每个不同的 y 坐标集一个。

    检查一维重叠真的非常简单。

    【讨论】:

    • 不是 那 容易 - 检查任何一对都是微不足道的,但对于 n 矩形,检查 每个 对将花费指数时间..跨度>
    • @tzaman:实际上,在一个维度上,这真的很容易。您按 O(N log N) 中的下限对它们进行排序,然后一次 O(N) 通过足以检测所有重叠。总时间复杂度为 O(N log N)。而且,正如我所说,这个“二维”问题简化为一组不相交的一维问题,这实际上会使整体解决方案更快(解决两个 X 大小的问题比一个大小为 2X 的问题)。
    • 当然,如果你知道算法 - 你的答案没有提到任何。我只是在评论一个简单的实现将是指数级的事实。
    • @tzaman:天真的解决方案将是 cuadratic,因为对数为 N*(N-1)/2。这个解决方案并不是那么幼稚,因为在最坏的情况下,任何算法都必须输出所有可能的对。
    【解决方案3】:

    您可以为此使用sweep line algorithm。本质上,将所有x-坐标转储到一个数组中,并用它们关联的矩形以及它们是该矩形的开始还是结束进行注释。然后只需对数组进行排序,并从头到尾扫描它,当您遇到它的起点时将每个矩形添加到“当前集合”中,并在找到终点时将其删除。任何时候您在当前集合中有多个矩形时,这些矩形就会重叠。

    【讨论】:

    • 我不明白 - 您正确识别了在 O(N log N) 时间内找到一维集合的交集的算法,但同时评论我的答案需要指数时间。
    • @Eyal - 我很高兴我的描述足够清楚,可以让你这样做! :)
    【解决方案4】:

    AWT 的 Rectangle 类确实有一些方法(例如检查 intersects)

    如果是 JLabel,那就更简单了。就做 rect1.getBounds().intersects(rect2.getBounds())

    【讨论】:

    • 谢谢,但考虑到这种情况下,蛮力太复杂了,n^n
    猜你喜欢
    • 2012-11-20
    • 2013-03-31
    • 2023-03-12
    • 2021-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-14
    相关资源
    最近更新 更多