LeetCode 252 会议室 III(Meeting Rooms III)题解与模拟面试

1. 引言

在现代办公和协作中,会议室的高效利用至关重要。LeetCode 252 题"会议室 III"要求我们在给定一组会议的时间区间后,计算同一时间段内需要开的最少会议室数量,以保证所有会议能顺利进行。本题不仅是经典的区间调度问题变形,也常见于资源分配、调度系统等实战场景。

标签:

  • 堆(Heap)
  • 排序(Sort)
  • 贪心(Greedy)

应用场景:

当系统中需动态分配资源(如服务器、房间、工程师),如何最小化资源数量而满足并发需求。该思路可推广到云计算资源调度、任务池管理等领域。


2. 题目描述

给定一个会议时间列表 intervals,其中 intervals[i] = [start_i, end_i] 表示第 i 场会议的开始时间和结束时间(不含结束端点)。请你计算并返回同时进行的会议的最大数量,也就是需要开的最少会议室数量。

示例函数签名(Python)

python 复制代码
def minMeetingRooms(intervals: List[List[int]]) -> int:
    pass
  • 输入 :二维整数数组 intervals,长度范围 [0, 10^4]
  • 输出:整数,最少会议室数量。

3. 示例分析

示例 1

复制代码
输入:[[0,30],[5,10],[15,20]]
输出:2
  • 解释:会议 [0,30] 与 [5,10] 时间重叠,需要 2 个房间;[15,20] 可复用 [5,10] 结束的房间。

示例 2

复制代码
输入:[[7,10],[2,4]]
输出:1
  • 解释:没有重叠,所有会议可在同一房间举行。

4. 问题建模与关键点

  1. 区间调度:本题属于「同时进行的区间最大重叠数」问题,经典解法是通过排序和优先队列统计并发量。
  2. 数据结构映射
    • 将每个会议的结束时间入堆,堆顶保存当前最早结束的会议。
    • 遍历开始时间,从堆中弹出所有已结束的会议,堆的大小即为当前并发会议数。

5. 解题思路

  1. 暴力枚举:对每一个分割点遍历所有区间,时间复杂度 O(n²),不推荐。
  2. 最小堆 + 排序
    • 按会议开始时间升序排序。
    • 使用最小堆维护各会议的结束时间。
    • 对每个会议,比较堆顶结束时间与当前会议开始时间:
      • 若堆顶 ≤ 当前开始,说明该会议室已空,将堆顶弹出并复用。
      • 否则需新开会议室。
    • 将当前会议的结束时间入堆。
    • 堆的大小即为所需会议室数。

为什么用堆?

  • 堆能在 O(log n) 内获得并更新最小结束时间,使整体复杂度保持 O(n log n)。

6. 算法流程

  1. intervals 为空,返回 0。
  2. 将所有会议按 start 升序排序。
  3. 初始化最小堆 heap = []
  4. 遍历 intervals
    • heap 非空且 heap[0] ≤ current_start,弹出堆顶(复用会议室)。
    • 否则无需操作(开新会议室)。
    • current_end 入堆。
  5. 遍历结束后,len(heap) 即为答案。

7. 代码实现

Python 版本

python 复制代码
from typing import List
import heapq

def minMeetingRooms(intervals: List[List[int]]) -> int:
    if not intervals:
        return 0
    # 按开始时间排序
    intervals.sort(key=lambda x: x[0])
    # 最小堆存储会议结束时间
    heap = []
    for start, end in intervals:
        # 如果有空闲会议室
        if heap and heap[0] <= start:
            heapq.heappop(heap)
        # 将当前会议结束时间加入堆中
        heapq.heappush(heap, end)
    return len(heap)

Java 版本

java 复制代码
import java.util.*;

class Solution {
    public int minMeetingRooms(int[][] intervals) {
        if (intervals == null || intervals.length == 0) return 0;
        Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for (int[] interval : intervals) {
            if (!heap.isEmpty() && heap.peek() <= interval[0]) {
                heap.poll();
            }
            heap.offer(interval[1]);
        }
        return heap.size();
    }
}

8. 复杂度分析

  • 时间复杂度:排序 O(n log n) + 遍历 n 次,每次堆操作 O(log n),总体 O(n log n)。
  • 空间复杂度:最坏情况下所有会议重叠,堆中有 n 个元素,O(n)。

9. 边界情况 & 优化思考

  1. 空列表:直接返回 0。
  2. 单个会议:返回 1。
  3. 所有会议无重叠:堆大小最高保持 1。
  4. 大量重叠:堆大小趋近 n。
  5. 优化内存有限场景:可将结束时间存入有序数组并使用双指针,牺牲部分时间性能以节省堆空间。

10. 总结

  • 本题为区间调度经典变形,核心在于统计最大并发区间数。
  • 利用最小堆维护最早结束时间,实现 O(n log n) 解决。
  • 模板可复用于「会议室 II」「区间交集」「区间插入」等相关题目。

11. 模拟面试

在面试中,本题常作为考察区间调度与堆应用的典型题目。

面试题型示例

  1. 题目复述:给定一组会议时间区间,求所需的最少会议室数。
  2. 核心考点:区间排序、最小堆维护、复杂度分析。
  3. 扩展追问
    • 如果数据量达到 10⁸ 条,如何优化?
    • 在内存受限时,双指针法如何替代堆?
    • 多线程场景下,如何并行调度?

答题建议流程

  • 明确题意:复述输入输出,例举小规模示例。
  • 思路阐述:先讲排序,再说明堆的作用和复用机制。
  • 手写伪码:关键操作集中在堆弹出与入堆两步。
  • 复杂度分析:O(n log n) 时间,O(n) 空间。
  • 优化讨论:根据场景提出双指针、流式处理或并行方案。
相关推荐
好易学·数据结构24 分钟前
可视化图解算法:二叉树的最大深度(高度)
数据结构·算法·二叉树·最大高度·最大深度·二叉树高度·二叉树深度
程序员-King.25 分钟前
day47—双指针-平方数之和(LeetCode-633)
算法·leetcode
阳洞洞31 分钟前
leetcode 1035. Uncrossed Lines
算法·leetcode·动态规划·子序列问题
小鹿鹿啊1 小时前
C语言编程--15.四数之和
c语言·数据结构·算法
rigidwill6661 小时前
LeetCode hot 100—最长有效括号
数据结构·c++·算法·leetcode·职场和发展
wuqingshun3141592 小时前
蓝桥杯17. 机器人塔
c++·算法·职场和发展·蓝桥杯·深度优先
图灵科竞社资讯组3 小时前
图论基础:图存+记忆化搜索
算法·图论
chuxinweihui3 小时前
数据结构——栈与队列
c语言·开发语言·数据结构·学习·算法·链表
爱编程的鱼3 小时前
C# 结构(Struct)
开发语言·人工智能·算法·c#