计算几何-扫描线算法

1、定义 

计算几何中,扫描线算法(Sweep Line Algorithm)或平面扫描算法(Plane Sweep Algorithm)是一种算法模式,虚拟扫描线或扫描面来解决欧几里德空间中的各种问题,一般被用来解决图形面积,周长等问题,是计算几何中的关键技术之一。


这种算法背后的想法是想象一条线(通常是一条垂直线)在平面上扫过或移动,在某些点停止。几何操作仅限于几何对象,无论何时停止,它们都与扫描线相交或紧邻扫描线,并且一旦线穿过所有对象,就可以获得完整的解。

2、个人理解

其实说起来比较简单,就是用一条线(横着的或者竖着的)从上到下,或者从左到右,这么水平扫描过去,需要记录中间状态,扫描完就能够得到最终结果,扫描线可以实现降维操作,类似于三维变成二维。思想是这个思想。

解题套路:

  1. 数据排序
  2. 扫描排序后的数据,聚合有共性的数据
  3. 用一个新的数据结构存聚合后的结果

3、做几道题来沉淀下

1、天际线问题
/**
 * 天际线 @link https://leetcode.cn/problems/the-skyline-problem/
 */
public class TheSkylineProblem {

    public List<List<Integer>> getSkyline(int[][] buildings) {
        // 降维,
        List<int[]> buildingList = new ArrayList<>();
        for (int[] building : buildings) {
            int[] leftBuild = new int[2];
            leftBuild[0] = building[0];
            leftBuild[1] = -building[2];
            buildingList.add(leftBuild);
            int[] rightBuild = new int[2];
            rightBuild[0] = building[1];
            rightBuild[1] = building[2];
            buildingList.add(rightBuild);
        }
        // 排个序,按X从小到大排,如果X相同,则比较高度,也是从小到大
        buildingList.sort((o1, o2) -> {
            if (o1[0] - o2[0] == 0) {
                return o1[1] - o2[1];
            } else {
                return o1[0] - o2[0];
            }
        });

        List<List<Integer>> res = new ArrayList<>();
        // 最大堆,记录高度状态
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Comparator.reverseOrder());
        int maxHeight = 0;
        // 结果必定是有个0值的
        priorityQueue.add(maxHeight);

        for (int[] build : buildingList) {
            // 左边界那么将这个高度入队,右边界删除即可
            if (build[1] < 0) {
                priorityQueue.add(-build[1]);
            } else {
                priorityQueue.remove(build[1]);
            }
            // 与当前最高度不同,则收集结果
            if (!priorityQueue.isEmpty() && maxHeight != priorityQueue.peek()) {
                maxHeight = priorityQueue.peek();
                List<Integer> tempList = new ArrayList<>();
                tempList.add(build[0]);
                tempList.add(maxHeight);
                res.add(tempList);
            }
        }
        return res;
    }
}

2、矩形面积 II

扫描线经典问题

package geometry.sweepline;

import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;

import java.util.*;

/**
 * 矩形面积2, https://leetcode.cn/problems/rectangle-area-ii/description/
 * 比较常见的利用扫描线来计算面积
 *
 * 竖着扫描,用区间Y的高度来作为扫描线,通过计算每个相邻的X差值*区间Y高度差,来收集面积结果
 * 中间状态就是:
 * 维护Y的区间高度,如果比上一个区间高就更新 Y的区间值
 * 同时也维护一个Y区间高度的差值
 * 
 * 需要注意精度值。。。 用 int 收集结果会超出
 */
public class RectangleAreaII {

    public int rectangleArea(int[][] rectangles) {
        // 降维,收集X坐标,并根据从小到大排序
        List<Integer> xList = new ArrayList<>();
        for (int[] rectangle : rectangles) {
            xList.add(rectangle[0]);
            xList.add(rectangle[2]);
        }

        Collections.sort(xList);
        int MOD = (int) (1.0E9 + 7);

        int res = 0;
        for (int i = 1; i < xList.size(); i++) {
            int xLeft = xList.get(i - 1);
            int xRight = xList.get(i);
            int interval = xRight - xLeft;
            if (interval == 0) {
                continue;
            }
            List<int[]> yList = new ArrayList<>();
            for (int[] rectangle : rectangles) {
                // 搞错了,这rectangle[2] 要大于 XRight 才记录,表示当前矩形区域覆盖 【xLeft,xRight】 这区域,最差也要是当前区域覆盖自己,所以有个=
                if (rectangle[0] <= xLeft && rectangle[2] >= xRight) {
                    yList.add(new int[]{rectangle[1], rectangle[3]});
                }
            }
            yList.sort((o1, o2) -> o1 != o2 ? o1[0] - o2[0] : o1[1] - o2[1]);
            long yTotal = 0;
            int lastDownY = -1;
            int lastUpY = -1;
            for (int[] ys : yList) {
                // 如果当前最低的高度都高于上次最高高度,那么更新全部信息
                if (ys[0] > lastUpY) {
                    // 先计算高度差值再赋值
                    yTotal += lastUpY - lastDownY;
                    lastDownY = ys[0];
                    lastUpY = ys[1];
                } else if (ys[1] > lastUpY){
                    // 只是当前最高高于上次最高
                    lastUpY = ys[1];
                }
            }
            yTotal += lastUpY - lastDownY;
            long value = yTotal * interval;
            res += value;
            res = res % MOD;
        }
        return res;
    }

    public static void main(String[] args) {
        List<int[]> rectangles = new ArrayList<>();
        rectangles.add(new int[]{0,0,2,2});
        rectangles.add(new int[]{1,0,2,3});
        rectangles.add(new int[]{1,0,3,1});
        RectangleAreaII rectangleAreaII = new RectangleAreaII();
        System.out.println("res:" + rectangleAreaII.rectangleArea(rectangles.toArray(new int[][]{})));
    }
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Bentley-Ottmann算法扫描线算法都是计算机科学领域中的几何算法,主要用于解决平面上的几何问题。 Bentley-Ottmann算法是一种用于求解线段交点问题的算法。它使用了扫描线的概念,通过扫描线从上到下遍历平面上的线段,并将其投影到水平的事件点序列上。在扫描线的过程中,通过维护一个有序的事件点集合以及一个有序的线段交点集合来找到所有的线段交点。该算法的时间复杂度为O((n+k) log n),其中n为线段的数量,k为交点的数量。 扫描线算法是一种通过扫描线的方式来解决一些几何问题的算法。其基本思想是将平面划分为许多水平的扫描线,并在每条扫描线上进行计算。算法从上到下按扫描线依次处理每个图形对象,记录下与当前扫描线相交的图形边界,并根据需要更新一些数据结构来保存相关信息。在处理完所有图形对象后,可以得到所需要的结果。扫描线算法主要应用于计算几何、计算机图形学等领域中的问题,例如求解多边形交集、寻找包含某一点的图形等。由于其简洁高效的特点,扫描线算法在计算机图形学中的应用非常广泛。 综上所述,Bentley-Ottmann算法扫描线算法都是用于解决平面上几何问题的算法。Bentley-Ottmann算法主要用于求解线段交点问题,而扫描线算法适用于处理一些特定的几何问题。这两种算法都是在计算几何和计算机图形学等领域中非常有用的工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值