扫描线算法总结

Number of Airplanes in the Sky

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

Example

样例 1:

输入: [(1, 10), (2, 3), (5, 8), (4, 7)]
输出: 3
解释: 
第一架飞机在1时刻起飞, 10时刻降落.
第二架飞机在2时刻起飞, 3时刻降落.
第三架飞机在5时刻起飞, 8时刻降落.
第四架飞机在4时刻起飞, 7时刻降落.
在5时刻到6时刻之间, 天空中有三架飞机.

思路:经典扫描线算法:把interval起飞和降落做为event,全部打散,按照时间排列,同时时间相等的,按照降落在前面,起飞在后面进行排序;最后再扫一遍,遇见start,count++,遇见end,count--,然后最后中间出现的最大值,就是题目所求。

/**
 * Definition of Interval:
 * public classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this.start = start;
 *         this.end = end;
 *     }
 * }
 */

public class Solution {
    /**
     * @param airplanes: An interval array
     * @return: Count of airplanes are in the sky.
     */
    
    private class Node {
        int time;
        int flag; // 1 take off, 0 is landing;
        public Node(int time, int flag) {
            this.time = time;
            this.flag = flag;
        }
    }
    
    private class NodeComparator implements Comparator<Node> {
        @Override
        public int compare(Node a, Node b) {
            if(a.time != b.time) {
                return a.time - b.time;
            } else {
                // 定义1是起飞,0是下降,这样如果按照大小排序,那么就是如果时间相等的情况下:下降的在前面,起飞的在后面。
                return a.flag - b.flag;
            }
        }
    }
     
    public int countOfAirplanes(List<Interval> airplanes) {
        if(airplanes == null || airplanes.size() == 0) {
            return 0;
        }
        int n = airplanes.size();
        Node[] nodes = new Node[n * 2];
        int index = 0;
        for(Interval interval : airplanes) {
            nodes[index++] = new Node(interval.start, 1);
            nodes[index++] = new Node(interval.end, 0);
        }
        
        Arrays.sort(nodes, new NodeComparator());
        int count = 0;
        int maxcount = 0;
        for(int i = 0; i < nodes.length; i++) {
            if(nodes[i].flag == 1) {
                count++;
            } 
            if(nodes[i].flag == 0) {
                count--;
            }
            maxcount = Math.max(count, maxcount);
        }
        return maxcount;
    }
}

Meeting Rooms II

给定一系列的会议时间间隔intervals,包括起始和结束时间[[s1,e1],[s2,e2],...] (si < ei),找到所需的最小的会议室数量。

Example

样例1

输入: intervals = [(0,30),(5,10),(15,20)]
输出: 2
解释:
需要两个会议室
会议室1:(0,30)
会议室2:(5,10),(15,20)

思路:题目跟上面飞机一模一样,代码都是一样的。

/**
 * Definition of Interval:
 * public classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this.start = start;
 *         this.end = end;
 *     }
 * }
 */

public class Solution {
    /**
     * @param intervals: an array of meeting time intervals
     * @return: the minimum number of conference rooms required
     */
    
    private class Meeting {
        public int time;
        public int flag;
        public Meeting (int time, int flag) {
            this.time = time;
            this.flag = flag; //  define 1 as start, 0 as end;
        }
    } 
    
    private class MeetingComparator implements Comparator<Meeting> {
        @Override
        public int compare(Meeting a, Meeting b) {
            if(a.time != b.time) {
                return a.time - b.time;
            } else {
                return a.flag - b.flag;
            }
        }
    }
     
    public int minMeetingRooms(List<Interval> intervals) {
        if(intervals == null || intervals.size() == 0) {
            return 0;
        }
        int n = intervals.size();
        Meeting[] meetings = new Meeting[n * 2];
        int index = 0;
        for(Interval interval : intervals) {
            meetings[index++] = new Meeting(interval.start, 1);
            meetings[index++] = new Meeting(interval.end, 0);
        }
        
        Arrays.sort(meetings, new MeetingComparator());
        int count = 0;
        int maxcount = 0;
        for(int i = 0; i < meetings.length; i++) {
            if(meetings[i].flag == 1) {
                count++;
            }
            if(meetings[i].flag == 0) {
                count--;
            }
            maxcount = Math.max(maxcount, count);
        }
        return maxcount;
        
    }
}

Time Intersection

目前有两个用户的有序在线时间序列,每一个区间记录了该用户的登录时间点x和离线时间点y,请找出这两个用户同时在线的时间段,输出的时间段请从小到大排序。你需要返回一个intervals的列表

Example

样例 1:

输入:seqA = [(1,2),(5,100)], seqB = [(1,6)]
输出:[(1,2),(5,6)]
解释:在 (1,2), (5,6) 这两个时间段内,两个用户同时在线。

样例 2:

输入:seqA = [(1,2),(10,15)], seqB = [(3,5),(7,9)]
输出:[]
解释:不存在任何时间段,两个用户同时在线。

Notice

  • 我们保证在线时间序列的长度 1 <= len <= 1e6
  • 我们保证在线时间序列是合法的,即对于某一个用户的在线时间序列,它的任意两个区间不相交。

思路:扫描线算法,注意的是,阈值发生变化的时候,收集数据,一定要在count = 2的时候收集。

/**
 * Definition of Interval:
 * public classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this.start = start;
 *         this.end = end;
 *     }
 * }
 */

public class Solution {
    /**
     * @param seqA: the list of intervals
     * @param seqB: the list of intervals
     * @return: the time periods
     */
    
    private class Node {
        public int time;
        public int flag;
        public Node(int time, int flag) {
            this.time = time;
            this.flag = flag;
        }
    } 
    
    private class NodeComparator implements Comparator<Node> {
        @Override
        public int compare(Node a, Node b) {
            if(a.time != b.time) {
                return a.time - b.time;
            } else {
                return a.flag - b.flag; // define flag 1 is online, 0 is offline;
            }
        }
    }
    
    public List<Interval> timeIntersection(List<Interval> seqA, List<Interval> seqB) {
        List<Interval> result = new ArrayList<Interval>();
        if(seqA == null || seqA.size() == 0 || seqB == null || seqB.size() == 0) {
            return result;
        }
        
        int n = seqA.size();
        int m = seqB.size();
        Node[] nodes = new Node[n * 2 + m * 2];
        int index = 0;
        for(int i = 0; i < n; i++) {
            nodes[index++] = new Node(seqA.get(i).start, 1);
            nodes[index++] = new Node(seqA.get(i).end, 0);
        }
        for(int i = 0; i < m; i++) {
            nodes[index++] = new Node(seqB.get(i).start, 1);
            nodes[index++] = new Node(seqB.get(i).end, 0);
        }
        
        Arrays.sort(nodes, new NodeComparator());
        int count = 0;
        int start = 0;
        int end = 0;
        for(int i = 0; i < nodes.length; i++) {
            if(nodes[i].flag == 1) {
                count++;
                if(count == 2) {
                    start = nodes[i].time;
                }
            }
            if(nodes[i].flag == 0) {
                if(count == 2) { //每次都是阈值发生变化的时候,收集interval;
                    end = nodes[i].time;
                    result.add(new Interval(start, end));
                    start = 0;
                    end = 0;
                }
                count--;
            }
        }
        return result;
    }
}

 思路2:这题因为数据是sorted,因为A和B都是sorted,那么跟merge array一样,可以用打擂台的方式,每次踢走一个。
如果Max(startA, startB) < Min(endA, endB)则加入相交的interval 看A, B end 谁大,谁留下,另外一个踢走;O(N+M)

// 因为数据是sorted,那么就按照overlap来扫描,也就是start取最大值,end取最小值,
// 如果start< end,表明有overlap,否则不会有overlap,同时end较小的扔掉,较大的留下;

/**
 * Definition of Interval:
 * public classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this.start = start;
 *         this.end = end;
 *     }
 * }
 */

public class Solution {
    /**
     * @param seqA: the list of intervals
     * @param seqB: the list of intervals
     * @return: the time periods
     */
    public List<Interval> timeIntersection(List<Interval> seqA, List<Interval> seqB) {
        List<Interval> result = new ArrayList<Interval>();
        if(seqA == null || seqA.size() == 0 || seqB == null || seqB.size() == 0) {
            return result;
        }
        
        int aIndex = 0;
        int bIndex = 0;
        
        // 因为数据是sorted,那么就按照overlap来扫描,也就是start取最大值,end取最小值,
        // 如果start< end,表明有overlap,否则不会有overlap,同时end较小的扔掉,较大的留下;
        while(aIndex < seqA.size() && bIndex < seqB.size()) {
            int start = Math.max(seqA.get(aIndex).start, seqB.get(bIndex).start);
            int end = Math.min(seqA.get(aIndex).end, seqB.get(bIndex).end);
            if(start < end) {
                result.add(new Interval(start, end));
            }
            if(seqA.get(aIndex).end < seqB.get(bIndex).end) {
                aIndex++;
            } else {
                bIndex++;
            }
        }
        return result;
    }
}

水平面上有 N 座大楼,每座大楼都是矩阵的形状,可以用一个三元组表示 (start, end, height),分别代表其在x轴上的起点,终点和高度。大楼之间从远处看可能会重叠,求出 N 座大楼的外轮廓线。

外轮廓线的表示方法为若干三元组,每个三元组包含三个数字 (start, end, height),代表这段轮廓的起始位置,终止位置和高度。

Example

样例1

输入:
[
    [1, 3, 3],
    [2, 4, 4],
    [5, 6, 1]
]
输出:
[
    [1, 2, 3],
    [2, 4, 4],
    [5, 6, 1]
]

思路:扫描线算法;注意:排序的时候,时间排序,然后起飞排在降落前面,然后高度倒序排,要让大的在前面,后面如果x相同的话,只计算大的,小的不用计算。另外,pq可能为空,为空就按照0来计算;

public class Solution {
    /**
     * @param buildings: A list of lists of integers
     * @return: Find the outline of those buildings
     */
    private class Node {
        private int x;
        private int height;
        private int flag;
        public Node (int x, int height, int flag) {
            this.x = x;
            this.height = height;
            this.flag = flag; // define flag = 1, start, flag = 0, end;
        }
    }
    
    private class NodeComparator implements Comparator<Node> {
        @Override
        public int compare(Node a, Node b) {
            if(a.x != b.x) {
                return a.x - b.x;
            } else {
                if(a.flag != b.flag) {
                    return b.flag - a.flag; // 让起飞在前面;
                } else {
                    return b.height - a.height;  
                    // 让高度最大的排前面;这样,同样x的,只考虑最高的;
                }
            }
        }
    }
    
    public List<List<Integer>> buildingOutline(int[][] buildings) {
        List<List<Integer>> lists = new ArrayList<List<Integer>>();
        if(buildings == null || buildings.length == 0 || buildings[0].length == 0) {
            return lists;
        }
        int n = buildings.length;
        Node[] nodes = new Node[n * 2];
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>(n * 2, Collections.reverseOrder());
        int index = 0;
        for(int i = 0; i < n; i++) {
            int start = buildings[i][0];
            int end = buildings[i][1];
            int height = buildings[i][2];
            
            nodes[index++] = new Node(start, height, 1);
            nodes[index++] = new Node(end, height, 0);
        }
        
        Arrays.sort(nodes, new NodeComparator());
        
        int preheight = 0;
        List<Node> candidates = new ArrayList<Node>();
        for(int i = 0; i < nodes.length; i++) {
            if(nodes[i].flag == 1) {
                pq.add(nodes[i].height);
            }
            if(nodes[i].flag == 0) {
                pq.remove(nodes[i].height);
            }
            int curheight = pq.isEmpty() ? 0 : pq.peek(); // 判断pq是否为空;
            if(preheight != curheight) {
                candidates.add(new Node(nodes[i].x, curheight, -1));
                preheight = curheight;
            }
        }
        convertCandiate(candidates, lists);
        return lists;
    }
    
    private void convertCandiate(List<Node> candidates, List<List<Integer>> lists) {
        List<Integer> list = new ArrayList<Integer>();
        for(int i = 0;  i < candidates.size() -1; i++) {
            Node first = candidates.get(i);
            Node second = candidates.get(i+1);
           
            if(first.height == 0 || first.x == second.x) {
                continue;
            } else {
                list.add(first.x);
                list.add(second.x);
                list.add(first.height);
                lists.add(new ArrayList<Integer>(list));
                list = new ArrayList<Integer>();
            }
        }
    }
}
发布了562 篇原创文章 · 获赞 13 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u013325815/article/details/103957911