高级数据结构之线段树与扫描线算法

1. 扫描线算法

1)原理:

平面扫描线算法通常由扫描线、事件点、当前扫描事件点集合构成:通过扫描线按照某一方向依次扫描,扫描事件点,检查事件点状态,然后新增或删除事件点以更新事件点集合。

2)看一道经典的问题:

Lintcode391. 数飞机

给出飞机的起飞和降落时间的列表,用 interval 序列表示. 请计算出天上同时最多有多少架飞机?

这道题看起来很简单,但是我的做法超时了:

public class Solution {
    /**
     * @param airplanes: An interval array
     * @return: Count of airplanes are in the sky.
     */
    public int countOfAirplanes(List<Interval> airplanes) {
        // write your code here
        if (airplanes.size() == 0) return 0;
        int maxTime = Integer.MIN_VALUE;
        for (Interval interval : airplanes) {
            maxTime = Math.max(maxTime, interval.end);
        }
        int[] timePoint = new int[maxTime + 1];
        for (int i = 1; i <= maxTime; i++) {
            int showTime = 0;
            for (Interval interval : airplanes) {
                if (interval.start <= i && interval.end > i) {
                    showTime++;
                }
            }
            timePoint[i] = showTime;
        }
        int res = Integer.MIN_VALUE;
        for (int i = 1; i <= maxTime; i++) {
            res = Math.max(res, timePoint[i]);
        }
        return res;
    }
}

这个解法是很细粒度的扫描,就是每个时刻都要扫描一次,所以当最长事件很大时,我开的数据就很大,就需要扫描很长的数组。

那么可否粗粒度的扫描呢?答那是可以的,就是只需要检查起点和终点。因为一段时间的构成就是起点和终点,那么涉及到事件点集合的变化就是起点的出现和终点的出现。

那么具体是如何变化的呢?想一想,如果一个起点出现,会发生什么,就说明当前时刻又有一个时间段出现了,如果终点出现,就说明当前时刻结束了一个时间段。

那么我们可以维护一个count,来记录实时事件点的变化。 当起点出现,count++;终点出现count--;哈,前提是这些时间段应该顺序排列。

ok,按这个思路改了代码,ac

public class Solution {
    /**
     * @param airplanes: An interval array
     * @return: Count of airplanes are in the sky.
     */
     
    class Point {
        int time;
        int flag;
        public Point(int t, int f) {
            time = t;
            flag = f;
        }
    }
    public int countOfAirplanes(List<Interval> airplanes) {
        // write your code here
        List<Point> points = new ArrayList<>();
        for (Interval interval : airplanes) {
            points.add(new Point(interval.start, 1));
            points.add(new Point(interval.end, 0));
        }
        Collections.sort(points, new Comparator<Point>(){
            public int compare(Point p1, Point p2) {
                if (p1.time == p2.time) return p1.flag - p2.flag;
                return p1.time - p2.time;
            }
        });
        int res = 0, count = 0;
        for (Point point : points) {
            if (point.flag == 1) count++;
            else count--;
            res = Math.max(res, count);
        }
        return res;
    }
}

参考资料:

http://openinx.github.io/2013/01/01/plane-sweep-thinking/

猜你喜欢

转载自www.cnblogs.com/shawshawwan/p/9160726.html