ToB企服应用市场:ToB评测及商务社交产业平台

标题: leetcode 扫描线专题 06-leetcode.252 meeting room 力扣.252 会议室 [打印本页]

作者: 立聪堂德州十三局店    时间: 2024-11-16 06:37
标题: leetcode 扫描线专题 06-leetcode.252 meeting room 力扣.252 会议室
扫描线专题

leetcode 扫描线专题 06-扫描线算法(Sweep Line Algorithm)
leetcode 扫描线专题 06-leetcode.218 the-skyline-problem 力扣.218 天涯线问题
leetcode 扫描线专题 06-leetcode.252 meeting room 力扣.252 会议室
leetcode 扫描线专题 06-leetcode.253 meeting room ii 力扣.253 会议室 II
题目

给定一个会议时间安排的数组 intervals ,每个会议时间都会包罗开始和结束的时间 intervals = [starti, endi] ,请你判定一个人是否能够参加这内里的全部会议。
示例 1:
输入:intervals = [[0,30],[5,10],[15,20]]
输出:false
示例 2:
输入:intervals = [[7,10],[2,4]]
输出:true
提示:

0  (a[0] == b[0]) ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]));        int ongoingMeetings = 0;        // 扫描事件列表,查抄会议的重叠情况        for (int[] event : events) {            ongoingMeetings += event[1];  // 加上当前事件的标志值            if (ongoingMeetings > 1) {                return false; // 有重叠会议            }        }        return true; // 没有重叠会议    }}[/code]为什么要如许排序?

在扫描线算法中,我们对事件的排序方式有特定的规则,如许排序是为了精确处置惩罚时间相同的开始和结束事件,制止错误的计数。
排序规则表明

在扫描线问题中,我们通常将每个区间拆分为两个事件,一个开始事件和一个结束事件。我们按照以下规则对所有事件举行排序:
为什么时间相同时优先处置惩罚结束事件?

在扫描线算法中,优先处置惩罚结束事件可以制止错误的重叠判定。
例如:
具体排序语句表明

以下代码是具体的排序语句:
  1. public static boolean canAttendMeetings(int[][] intervals) {
  2.     int n = intervals.length;
  3.     // 双重循环检查每一对会议是否重叠
  4.     for (int i = 0; i < n; i++) {
  5.         for (int j = i + 1; j < n; j++) {
  6.             // 检查会议 intervals[i] 和 intervals[j] 是否重叠
  7.             if (intervals[i][1] > intervals[j][0] && intervals[j][1] > intervals[i][0]) {
  8.                 return false; // 找到重叠会议,返回 false
  9.             }
  10.         }
  11.     }
  12.     // 没有重叠会议
  13.     return true;
  14. }
复制代码
示例说明

假设有三个会议时间:[5, 10], [5, 12], [10, 15]。拆解事件后:
  1. public static boolean canAttendMeetings(int[][] intervals) {
  2.     int n = intervals.length;
  3.     Arrays.sort(intervals, Comparator.comparingInt(o -> o[0]));
  4.     // 双重循环检查每一对会议是否重叠
  5.     for (int i = 1; i < n; i++) {
  6.         // 本次开始 上一次还没有结束
  7.         if(intervals[i][0] < intervals[i-1][1]) {
  8.             return false;
  9.         }
  10.     }
  11.     // 没有重叠会议
  12.     return true;
  13. }
复制代码
排序结果为:
  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.List;
  4. public class MeetingScheduler {
  5.     public static boolean canAttendMeetings(int[][] intervals) {
  6.         List<int[]> events = new ArrayList<>();
  7.         
  8.         // 将每个会议的开始和结束时间作为事件存储
  9.         for (int[] interval : intervals) {
  10.             events.add(new int[]{interval[0], 1});  // 会议开始时间 +1
  11.             events.add(new int[]{interval[1], -1}); // 会议结束时间 -1
  12.         }
  13.         // 按时间排序,如果时间相同,优先处理结束事件
  14.         Collections.sort(events, (a, b) -> (a[0] == b[0]) ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]));
  15.         int ongoingMeetings = 0;
  16.         // 扫描事件列表,检查会议的重叠情况
  17.         for (int[] event : events) {
  18.             ongoingMeetings += event[1];  // 加上当前事件的标记值
  19.             if (ongoingMeetings > 1) {
  20.                 return false; // 有重叠会议
  21.             }
  22.         }
  23.         return true; // 没有重叠会议
  24.     }
  25. }
复制代码
综上所述

按这种顺序排序,可以确保时间相同的情况下优先结束事件,从而精确地处置惩罚重叠区间并维护扫描线的计数。
小结

看起来这个扫描线算法还是比较有趣的,后面我们专门一个系列学习一下。
v4-使用优先队列(最小堆)

思路

这种方法通过维护一个最小堆来跟踪当前正在举行的会议,适合在更复杂的情况下进一步扩展。
例如,盘算一个人最多能同时参加的会议数量等。
步调
实现
  1. Collections.sort(events, (a, b) -> (a[0] == b[0]) ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]));
复制代码
为啥要用优先级队列?

优先级队列(PriorityQueue)在扫描线算法中的作用重要是动态地管理和维护当前活动的区间或事件。
即使我们先对所有事件举行了排序,使用优先级队列在扫描过程中仍旧可以有用地处置惩罚区间重叠、活动计数等问题。
为什么排序之后不直接对比而要用优先级队列?

虽然排序能够帮助我们按时间顺序处置惩罚事件,但在扫描过程中,动态维护当前活动的区间或事件是很紧张的。
这就是优先级队列的作用。它可以在扫描过程中高效地维护哪些区间或事件仍旧处于“活泼”状态。
优先级队列的关键作用

典型的使用场景

以经典的 会议室问题(LC 253)为例:
1. 事件排序

我们起首将所有的开始事件和结束事件按照时间举行排序。如果时间相同,我们优先处置惩罚结束事件。排序后的事件列表可能如下:
  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.List;
  4. public class MeetingScheduler {
  5.     public static boolean canAttendMeetings(int[][] intervals) {
  6.         List<int[]> events = new ArrayList<>();
  7.         
  8.         // 将每个会议的开始和结束时间作为事件存储
  9.         for (int[] interval : intervals) {
  10.             events.add(new int[]{interval[0], 1});  // 会议开始时间 +1
  11.             events.add(new int[]{interval[1], -1}); // 会议结束时间 -1
  12.         }
  13.         // 按时间排序,如果时间相同,优先处理结束事件
  14.         Collections.sort(events, (a, b) -> (a[0] == b[0]) ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]));
  15.         int ongoingMeetings = 0;
  16.         // 扫描事件列表,检查会议的重叠情况
  17.         for (int[] event : events) {
  18.             ongoingMeetings += event[1];  // 加上当前事件的标记值
  19.             if (ongoingMeetings > 1) {
  20.                 return false; // 有重叠会议
  21.             }
  22.         }
  23.         return true; // 没有重叠会议
  24.     }
  25. }
复制代码
2. 使用优先级队列维护活动会议

在扫描事件时,我们必要维护当前活泼的会议。例如,我们可能希望知道目前有多少会议在同时举行。每当遇到一个开始事件(+1),我们将其加入优先级队列;每当遇到一个结束事件(-1),我们将其从优先级队列中移除。
使用优先级队列可以确保:
3. 最小会议室数

每次扫描到一个开始事件时,我们会将当前活动的会议数增加;每次扫描到结束事件时,我们会减少当前活动的会议数。我们通过维护当前活动会议的数量,最终可以得到最大并发的会议数,也就是所需的最小会议室数。
这时,优先级队列的作用是:
4. 具体实现细节

假设我们使用一个优先级队列来保存当前正在举行的会议的结束时间。每当遇到开始事件时,我们将该会议的结束时间加入队列;每当遇到结束事件时,我们将其从队列中移除。
优先级队列在这里的作用:

总结

排序后的事件只是帮助我们按时间顺序扫描事件。
优先级队列则通过动态管理活泼区间(如会议的结束时间等),保证我们能高效地判定每个时候的活动状态,快速获恰当前活泼区间的数量或其它信息。
在这种问题中,排序加优先级队列的组合为扫描线算法提供了高效且灵活的办理方案。
小结

不得不说,用数组替换 map 的方法确实令人叹为观止。
那么,我们前面的 two-sum 是不是也可以如许优化?
开源地址

为了便于大家学习,所有实现均已开源。欢迎 fork + star~
https://github.com/houbb/leetcode

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4