0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

时间调度问题的千层套路

算法与数据结构 来源:labuladong 作者:labuladong 2022-08-08 14:14 次阅读

之前面试,被问到一道非常经典且非常实用的算法题目:会议室安排问题。

力扣上类似的问题是会员题目,你可能没办法做,但对于这种经典的算法题,掌握思路还是必要的。

先说下题目,给你输入若干形如[begin, end]的区间,代表若干会议的开始时间和结束时间,请你计算至少需要申请多少间会议室。

函数签名如下:

//返回需要申请的会议室数量
intminMeetingRooms(int[][]meetings);

比如给你输入meetings = [[0,30],[5,10],[15,20]],算法应该返回 2,因为后两个会议和第一个会议时间是冲突的,至少申请两个会议室才能让所有会议顺利进行。

如果会议之间的时间有重叠,那就得额外申请会议室来开会,想求至少需要多少间会议室,就是让你计算同一时刻最多有多少会议在同时进行。

换句话说,如果把每个会议的起止时间看做一个线段区间,那么题目就是让你求最多有几个重叠区间,仅此而已。

对于这种时间安排的问题,本质上讲就是区间调度问题,十有八九得排序,然后找规律来解决。

题目延伸

我们之前写过很多区间调度相关的文章,这里就顺便帮大家梳理一下这类问题的思路:

第一个场景,假设现在只有一个会议室,还有若干会议,你如何将尽可能多的会议安排到这个会议室里?

这个问题需要将这些会议(区间)按结束时间(右端点)排序,然后进行处理,详见前文贪心算法做时间管理

第二个场景,给你若干较短的视频片段,和一个较长的视频片段,请你从较短的片段中尽可能少地挑出一些片段,拼接出较长的这个片段。

这个问题需要将这些视频片段(区间)按开始时间(左端点)排序,然后进行处理,详见前文剪视频剪出一个贪心算法

第三个场景,给你若干区间,其中可能有些区间比较短,被其他区间完全覆盖住了,请你删除这些被覆盖的区间。

这个问题需要将这些区间按左端点排序,然后就能找到并删除那些被完全覆盖的区间了,详见前文删除覆盖区间

第四个场景,给你若干区间,请你将所有有重叠部分的区间进行合并。

这个问题需要将这些区间按左端点排序,方便找出存在重叠的区间,详见前文合并重叠区间

第五个场景,有两个部门同时预约了同一个会议室的若干时间段,请你计算会议室的冲突时段。

这个问题就是给你两组区间列表,请你找出这两组区间的交集,这需要你将这些区间按左端点排序,详见前文区间交集问题

第六个场景,假设现在只有一个会议室,还有若干会议,如何安排会议才能使这个会议室的闲置时间最少?

这个问题需要动动脑筋,说白了这就是个 0-1 背包问题的变形:

会议室可以看做一个背包,每个会议可以看做一个物品,物品的价值就是会议的时长,请问你如何选择物品(会议)才能最大化背包中的价值(会议室的使用时长)?

当然,这里背包的约束不是一个最大重量,而是各个物品(会议)不能互相冲突。把各个会议按照结束时间进行排序,然后参考前文0-1 背包问题详解的思路即可解决,等我以后有机会可以写一写这个问题。

第七个场景,就是本文想讲的场景,给你若干会议,让你合理申请会议室。

好了,举例了这么多,来看看今天的这个问题如何解决。

题目分析

重复一下题目的本质:

给你输入若干时间区间,让你计算同一时刻「最多」有几个区间重叠

题目的关键点在于,给你任意一个时刻,你是否能够说出这个时刻有几个会议在同时进行?

如果可以做到,那我遍历所有的时刻,找个最大值,就是需要申请的会议室数量。

有没有一种数据结构或者算法,给我输入若干区间,我能知道每个位置有多少个区间重叠?

老读者肯定可以联想到之前说过的一个算法技巧:差分数组技巧

把时间线想象成一个初始值为 0 的数组,每个时间区间[i, j]就相当于一个子数组,这个时间区间有一个会议,那我就把这个子数组中的元素都加一。

最后,每个时刻有几个会议我不就知道了吗?我遍历整个数组,不就知道至少需要几间会议室了吗?

举例来说,如果输入meetings = [[0,30],[5,10],[15,20]],那么我们就给数组中[0,30],[5,10],[15,20]这几个索引区间分别加一,最后遍历数组,求个最大值就行了。

还记得吗,差分数组技巧可以在 O(1) 时间对整个区间的元素进行加减,所以可以拿来解决这道题。

不过,这个解法的效率不算高,所以我这里不准备具体写差分数组的解法,参照差分数组技巧的原理,有兴趣的读者可以自己尝试去实现。

基于差分数组的思路,我们可以推导出一种更高效,更优雅的解法

我们首先把这些会议的时间区间进行投影:

208311d6-16c9-11ed-ba43-dac502259ad0.jpg

红色的点代表每个会议的开始时间点,绿色的点代表每个会议的结束时间点。

现在假想有一条带着计数器的线,在时间线上从左至右进行扫描,每遇到红色的点,计数器count加一,每遇到绿色的点,计数器count减一:

20943a06-16c9-11ed-ba43-dac502259ad0.jpg

这样一来,每个时刻有多少个会议在同时进行,就是计数器count的值,count的最大值,就是需要申请的会议室数量

对差分数组技巧熟悉的读者一眼就能看出来了,这个扫描线其实就是差分数组的遍历过程,所以我们说这是差分数组技巧衍生出来的解法。

代码实现

那么,如何写代码实现这个扫描的过程呢?

首先,对区间进行投影,就相当于对每个区间的起点和终点分别进行排序:

20aa2898-16c9-11ed-ba43-dac502259ad0.jpg

intminMeetingRooms(int[][]meetings){
intn=meetings.length;
int[]begin=newint[n];
int[]end=newint[n];
//把左端点和右端点单独拿出来
for(inti=0;i< n; i++) {
        begin[i] = meetings[i][0];
end[i]=meetings[i][1];
}
//排序后就是图中的红点
Arrays.sort(begin);
//排序后就是图中的绿点
Arrays.sort(end);

//...
}

然后就简单了,扫描线从左向右前进,遇到红点就对计数器加一,遇到绿点就对计数器减一,计数器count的最大值就是答案:

intminMeetingRooms(int[][]meetings){
intn=meetings.length;
int[]begin=newint[n];
int[]end=newint[n];
for(inti=0;i< n; i++) {
        begin[i] = meetings[i][0];
end[i]=meetings[i][1];
}
Arrays.sort(begin);
Arrays.sort(end);

//扫描过程中的计数器
intcount=0;
//双指针技巧
intres=0,i=0,j=0;
while(i< n && j < n) {
        if(begin[i]< end[j]) {
            //扫描到一个红点
count++;
i++;
}else{
//扫描到一个绿点
count--;
j++;
}
//记录扫描过程中的最大值
res=Math.max(res,count);
}

returnres;
}

这里使用的是双指针技巧,你可以认为指针i就是那根扫描线,根据i, j的相对位置就可以模拟扫描线前进的过程。

至此,这道题就做完了。当然,这个题目也可以变形,比如给你若干会议,问你k个会议室够不够用,其实你套用本文的解法代码,也可以很轻松解决。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4616

    浏览量

    93030
  • 数组
    +关注

    关注

    1

    文章

    417

    浏览量

    25974

原文标题:时间调度问题的千层套路

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux之CPU调度策略和CPU亲和性

    决定在某一时间点上哪个进程在运行。调度器必须平衡几个选项: 快速决定下一个该运行的进程 进程可以公平的活动 CPU 时间,但高优先级的进程会活动更多的运行时间并且可以抢占低优先级的进程
    的头像 发表于 12-05 16:38 493次阅读
    Linux之CPU<b class='flag-5'>调度</b>策略和CPU亲和性

    算力调度的基础知识

    编者按 “算力调度”的概念,这几年越来越多的被提及。刚听到这个概念的时候,我脑海里一直拐不过弯。作为底层芯片出身的我,一直认为:算力是硬件的服务器和集群,他在某个地方,就是固定的;根本就不存在算力
    的头像 发表于 11-27 17:13 208次阅读
    算力<b class='flag-5'>调度</b>的基础知识

    Linux调度器的核心scheduler_tick介绍

    scheduler_tick在Linux内核中扮演着关键角色。它不仅负责处理定时器中断和更新系统时间,还记录进程的运行时间,并决定是否需要进行任务切换。通过这些功能,scheduler_tick有效保障了系统的时间管理和任务
    的头像 发表于 08-22 14:54 447次阅读

    FPGA-5G通信算法的基本套路

    》、《信号与系统》、《数字通信原理》、《数字信号处理》、《现代信号处理》、《电磁场与电磁波》、《信息论与编码》等一系列课程磨练过。历经帆,我们再来看看,通信系统的设计,套路在哪?从理论到实践,有几条街的距离
    发表于 08-15 17:34

    深入探讨Linux的进程调度

    Linux操作系统作为一个开源且广泛应用的操作系统,其内核设计包含了许多核心功能,而进程调度器(Scheduler)就是其中一个至关重要的模块。进程调度器负责决定在任何给定的时刻哪个进程可以运行
    的头像 发表于 08-13 13:36 959次阅读
    深入探讨Linux的进程<b class='flag-5'>调度</b>器

    车辆调度系统

    【铭迹创新】车辆调度系统分为隧道内和隧道外,隧道内应用UWB精确定位、RFID等技术,对隧道内施工车辆进行定位及区分行车道,综合应用红绿灯,LED屏幕、广播等技术对隧道内车辆进行有效的施工调度。并可
    的头像 发表于 05-31 15:59 531次阅读
    车辆<b class='flag-5'>调度</b>系统

    TSMaster 自定义 LIN 调度表编程指导

    LIN(LocalInterconnectNetwork)协议调度表是用于LIN总线通信中的消息调度的一种机制,我们收到越来越多来自不同用户希望能够通过接口实现自定义LIN调度表的需求。所以在
    的头像 发表于 05-11 08:21 696次阅读
    TSMaster 自定义 LIN <b class='flag-5'>调度</b>表编程指导

    浅析FreeRTOS任务调度器的三种调度算法和应用

    FreeRTOS在MCU领域应用非常广泛,今天就给大家讲解一下FreeRTOS调度器中的三种调度算法,以及在瑞萨RZ/T2L MPU中的应用。
    的头像 发表于 05-10 14:02 7552次阅读
    浅析FreeRTOS任务<b class='flag-5'>调度</b>器的三种<b class='flag-5'>调度</b>算法和应用

    如何实现上位机灵活调度下位机?

    把下位机各个功能做成不同的函数,然后通过上位机来调度各个函数执行,比如说上位机发送一个协议让LED1,LED2,LED3各亮一段时间,顺序,时间由上位机发来的协议决定,不知道下位机怎么来实现这个逻辑。
    发表于 04-25 07:21

    什么是PCB叠?PCB叠设计原则

    对于信号,通常每个信号都与内电直接相邻,与其他信号有有效的隔离,以减小串扰。在设计过程中,可以考虑多层参考地平面,以增强电磁吸收能力。
    的头像 发表于 04-10 16:02 2491次阅读
    什么是PCB叠<b class='flag-5'>层</b>?PCB叠<b class='flag-5'>层</b>设计原则

    什么是时间片轮转调度时间片轮转调度算法基本原理

    进程切换时间一定的情况下,如果时间片长度设定的越小时,这种浪费更明显。所以,时间片长度与CPU利用率是一对不可调和的矛盾,必须处理好它们之间的关系。
    的头像 发表于 03-22 14:54 3568次阅读

    FreeRTOS任务调度器的三种调度算法讲解(下)

    配置如下时,调度算法就会变成不带时间片的抢占式调度
    的头像 发表于 03-21 13:46 2944次阅读
    FreeRTOS任务<b class='flag-5'>调度</b>器的三种<b class='flag-5'>调度</b>算法讲解(下)

    鸿蒙OS 分布式任务调度

    鸿蒙OS 分布式任务调度概述 在 HarmonyO S中,分布式任务调度平台对搭载 HarmonyOS 的多设备构筑的“超级虚拟终端”提供统一的组件管理能力,为应用定义统一的能力基线、接口
    的头像 发表于 01-29 16:50 511次阅读

    Linux的Deadline实时调度算法

    每个任务都有一个高精度定时器(sched_dl_entity 结构的 dl_timer 字段),其超时时间为任务的调度周期。当定时器触发时,便会调用 dl_task_timer() 函数来处理定时器事件。
    发表于 01-24 13:44 918次阅读
    Linux的Deadline实时<b class='flag-5'>调度</b>算法

    配网调度自动化自愈系统的设计与实现

    针对配网调度现状进行相应需求分析并提出配置方案,着重研究和设计了配网自愈系统。使用配网自愈系统可以减少故障处理时调度员等候操作人员到达现场浪费的时间,由主站控制分段开关及环网联络开关,实现故障上、下游恢复供电,最终达到快速、准确
    的头像 发表于 01-16 14:08 923次阅读
    配网<b class='flag-5'>调度</b>自动化自愈系统的设计与实现