LeetCode——587 安装栅栏(JAVA)

这篇博客介绍了如何解决一个二维花园中用坐标表示的树的围栏问题,目标是最短绳子围起所有树。博主使用了Jarvis算法,详细阐述了算法思路,包括从最左端的点开始,寻找其他点形成凸包,并处理特殊情况如单一树的情况。博主还分享了代码实现,特别注意了处理同一直线上的点和算法结束条件。
摘要由CSDN通过智能技术生成

在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。

示例 1:

输入: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
输出: [[1,1],[2,0],[4,2],[3,3],[2,4]]

解释:
在这里插入图片描述

示例 2:

输入: [[1,2],[2,2],[4,2]]
输出: [[1,2],[2,2],[4,2]]

在这里插入图片描述
注意:

  • 所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
  • 输入的整数在 0 到 100 之间。
  • 花园至少有一棵树。
  • 所有树的坐标都是不同的。
  • 输入的点没有顺序。输出顺序也没有要求。

思路

第一次碰到凸包的问题,这里用的Jarvis算法,因为理解起来比较简单。但是在具体手写代码实现的过程中,还是碰到了不少的问题。

Jarvis算法的主要思路就是,先从最左端的点开始(作为p),然后寻找其他点作为q,在固定完pq之后,再在剩余的点中寻找r
然后,计算 p q → \overrightarrow{pq} pq q r → \overrightarrow{qr} qr 的叉积:
如果叉积>=0,则说明此时的点r p q → \overrightarrow{pq} pq 的左侧(右手定则,此时,从 p q → \overrightarrow{pq} pq q r → \overrightarrow{qr} qr 逆时针旋转的角度<=180度);反之,则说明此时的点r p q → \overrightarrow{pq} pq 的右侧。

因此,为了能找到一个凸包,就需要确定此时形成的 p q → \overrightarrow{pq} pq ,它能否将所有的r都放在同一侧(本题是左侧)。如果能,则将这个q作为下一个pp是答案集合中的点),并进入下一轮循环寻找新的点q;如果不能,则需要继续寻找点q
至于算法结束的位置,就是当成为一个环时,算法就结束了。也就是说,当寻找得到的点q又是起始位置最左端的点时,结束这个算法。

然后说一下本题可能遇到的坑点和我的解决办法:

  1. 输入可能只有一个点,这种情况特判即可,即:
if(trees.length==1) return trees;
  1. 如果直接对输入的坐标按照Jarvis算法求凸包,可能会漏掉在同一条直线上的情况,比如在示例1中,可能会漏掉(3, 3)这个点,因为它在(4, 2)(2, 4)所构成的直线上。
    我的解决办法是:如果碰到一个可行的点q,那么,将它从未选择的点中删去后,将这些未选择的点,按照到点q的距离从小到大排序,这样,就不会漏掉一条直线上的点了。
    比如,在我寻找到(4, 2)作为点q这样一个可行点后,我对剩下的(2, 2)(3, 3)(2, 4)来排个序,靠近(4, 2)的点我先去遍历,这样的话,就不会漏掉可行解了。
  2. 因为算法结束的位置是再次寻找到起点(也就是最左端的点),因此,在上述排序的时候,我们需要把起点抛开,然后排完序再加进去(也就是让起点始终保持在最后一个位置)。否则的话,在示例2中,如果找到(2, 2)这个点后,按照距离排序,就会直接找到起点(1, 2)从而结束整个算法,这样就漏掉了(4, 2)这个点。
  3. 关于起点:最左端的点指的是横坐标(x)最小的那个点,而不应该是最左下角的点(xy都小)。

代码

class Solution {
    static class Point{
        int x;
        int y;
        double dis; // 其他点到p的距离
        Point(){}
        Point(int xx, int yy){
            x = xx;
            y = yy;
        }
        public int getX(){
            return x;
        }
        public int getY(){
            return y;
        }
    }
    public double dis(Point p, Point q){
        int x1 = p.getX();
        int x2 = q.getX();
        int y1 = p.getY();
        int y2 = q.getY();
        return Math.sqrt(Math.pow((x1-x2), 2)+Math.pow((y1-y2), 2));
    }
    public boolean cross(Point p, Point q, Point r){
        int x1 = q.getX()-p.getX();
        int y1 = q.getY()-p.getY();
        int x2 = r.getX()-q.getX();
        int y2 = r.getY()-q.getY();
        int result = x1*y2 - y1*x2;
        return result >= 0;
    }
    public int[][] outerTrees(int[][] trees) {
        if(trees.length==1) return trees;
        else{
            List<Point> choose = new ArrayList<>();
            List<Point> notChoose = new ArrayList<>();
            int leftX = 110, leftY = 110;
            Point left = new Point();
            for(int[] tree : trees){
                int tmpX = tree[0];
                int tmpY = tree[1];
                Point tmp = new Point(tmpX, tmpY);
                notChoose.add(tmp);
                if (tmpX < leftX) {
                    leftX = tmpX;
                    leftY = tmpY;
                    left = tmp;
                }
            }
            Point p = left;
            Comparator<Point> comparator = new Comparator<Point>() {
                @Override
                public int compare(Point o1, Point o2) {
                    if(o1.dis<o2.dis) return -1;
                    else if(o1.dis>o2.dis) return 1;
                    else return 0;
                }
            };
            notChoose.remove(left);
            for(Point point : notChoose){
                point.dis = dis(p, point);
            }
            notChoose.sort(comparator);
            notChoose.add(left);
            for(int i=0;i<notChoose.size();i++){
                Point q = notChoose.get(i);
                if(q==p) continue;
                boolean flag = true;
                for(Point r : notChoose){
                    if(q!=r){
                        if(!cross(p, q, r)) flag = false;
                    }
                }
                if(flag){
                    choose.add(q);
                    notChoose.remove(q);
                    if(q.getX()==leftX && q.getY()==leftY) break; //算法结束
                    p = q;
                    i = -1;
                    notChoose.remove(left);
                    for(Point point : notChoose){
                        point.dis = dis(p, point);
                    }
                    notChoose.sort(comparator);
                    notChoose.add(left);
                }
            }
            int[][] result = new int[choose.size()][2];
            for(int i=0;i<choose.size();i++){
                int tmpX = choose.get(i).getX();
                int tmpY = choose.get(i).getY();
                result[i][0] = tmpX;
                result[i][1] = tmpY;
            }
            return result;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值