LeetCode 729. My Calendar I【设计;有序集合,二分查找;线段树】中等

news2025/1/19 8:26:29

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。


实现一个 MyCalendar 类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。

当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。

日程可以用一对整数 start 和 end 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为,  start <= x < end 。

实现 MyCalendar 类:

  • MyCalendar() 初始化日历对象。
  • boolean book(int start, int end) 如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true 。否则,返回 false 并且不要将该日程安排添加到日历中。

示例:

输入:
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
输出:
[null, true, false, true]

解释:

MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False ,这个日程安排不能添加到日历中,因为时间 15 已经被另一个日程安排预订了。
myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日历中,因为第一个日程安排预订的每个时间都小于 20 ,且不包含时间 20 。

提示:

  • 0 <= start < end <= 10^9
  • 每个测试用例,调用 book 方法的次数最多不超过 1000 次。

这道题来自:Weekly Contest 59,类似处理区间模型的题有:

  • 715. Range 模块
  • 2276. 统计区间中的整数数目
  • 731. My Calendar II
  • 732. My Calendar III

解法1 直接遍历

我们记录下所有已经预订的课程安排区间,当我们预订新的区间 [ start , end ) [\textit{start}, \textit{end}) [start,end) 时,此时检查当前已经预订的每个日程安排是否与新日程安排冲突。若不冲突,则可以添加新的日程安排。

  • 对于两个区间 [ s 1 , e 1 ) [s_1, e_1) [s1,e1) [ s 2 , e 2 ) [s_2, e_2) [s2,e2) ,如果二者没有交集,则此时应当满足 s 1 ≥ e 2 s_1 \ge e_2 s1e2 或者 s 2 ≥ e 1 s_2 \ge e_1 s2e1 ,这就意味着如果满足 s 1 < e 2 s_1 < e_2 s1<e2 并且 s 2 < e 1 s_2 < e_1 s2<e1
class MyCalendar {
private:
    vector<pair<int, int>> booked;
public:
    bool book(int start, int end) {
        for (auto &[l, r] : booked)
            if (l < end && start < r) return false;
        booked.emplace_back(start, end);
        return true;
    }
};

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2) ,其中 n n n 表示日程安排的数量。由于每次在进行预订时,都需要遍历所有已经预订的行程安排。
  • 空间复杂度: O ( n ) O(n) O(n) ,其中 n n n 表示日程安排的数量。需要保存所有已经预订的行程。

解法2 二分查找

如果我们按时间顺序维护日程安排,则可以通过二分查找日程安排的情况来检查新日程安排是否可以预订,若可以预订则在排序结构中更新插入日程安排。

需要一个数据结构能够保持元素排序和支持快速插入,可以用 TreeSet \texttt{TreeSet} TreeSet 来构建。对于给定的区间 [ s t a r t , e n d ) [start,end) [start,end) ,我们每次查找起点大于等于 end \textit{end} end 的第一个区间 [ l 1 , r 1 ) [l_1,r_1) [l1,r1) ,同时紧挨着 [ l 1 , r 1 ) [l_1,r_1) [l1,r1) 的前一个区间为 [ l 2 , r 2 ) [l_2,r_2) [l2,r2) ,此时如果满足 r 2 ≤ start < end ≤ l 1 r_2 \le \textit{start} < \textit{end} \le l_1 r2start<endl1 ,则该区间可以预订。

class MyCalendar {
    set<pair<int, int>> booked;
public:
    bool book(int start, int end) {
        auto it = booked.lower_bound({end, 0});
        if (it == booked.begin() || (--it)->second <= start) {
            booked.emplace(start, end);
            return true;
        }
        return false;
    }
};

复杂度分析:

  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn) ,其中 n n n 表示日程安排的数量。由于每次在进行预订时,都需要进行二分查找,需要的时间为 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度: O ( n ) O(n) O(n) ,其中 n n n 表示日程安排的数量。需要保存所有已经预订的行程。

解法3 线段树

利用线段树,假设我们开辟了数组 arr [ 0 , ⋯   , 1 0 9 ] \textit{arr}[0,\cdots, 10^9] arr[0,,109] ,初始时每个元素的值都为 0 0 0 ,对于每次行程预订的区间 [ s t a r t , e n d ) [start, end) [start,end) ,则我们将区间中的元素 arr [ start , ⋯   , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1] arr[start,,end1] 中的每个元素都标记为 1 1 1 ,每次调用 book \texttt{book} book 时,我们只需要检测 arr [ start , ⋯   , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1] arr[start,,end1] 区间内是否有元素被标记为 1 1 1 。实际我们不必实际开辟数组 arr \textit{arr} arr ,可采用动态线段树,懒标记 lazy \textit{lazy} lazy 标记区间 [ l , r ] [l,r] [l,r] 已经被预订, tree \textit{tree} tree 记录区间 [ l , r ] [l,r] [l,r] 的是否存在标记为 1 1 1 的元素。

每次进行 book \texttt{book} book 操作时,首先判断区间 [ start , ⋯   , end − 1 ] [\textit{start},\cdots,\textit{end}-1] [start,,end1] 是否存在元素被标记,如果存在被标记为 1 1 1 的元素,则表明该区间不可预订;否则,则将可以预订。预订完成后,将 arr [ start , ⋯   , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1] arr[start,,end1] 进行标记为 1 1 1 ,并同时更新线段树。

class MyCalendar {
    unordered_set<int> tree, lazy;
public:
    bool query(int start, int end, int l, int r, int idx) {
        if (r < start || end < l) {
            return false;
        }
        /* 如果该区间已被预订,则直接返回 */
        if (lazy.count(idx)) {
            return true;
        }
        if (start <= l && r <= end) {
            return tree.count(idx);
        }
        int mid = (l + r) >> 1;
        return query(start, end, l, mid, 2 * idx) ||
               query(start, end, mid + 1, r, 2 * idx + 1);
    }

    void update(int start, int end, int l, int r, int idx) {
        if (r < start || end < l) {
            return;
        }
        if (start <= l && r <= end) {
            tree.emplace(idx);
            lazy.emplace(idx);
        } else {
            int mid = (l + r) >> 1;
            update(start, end, l, mid, 2 * idx);
            update(start, end, mid + 1, r, 2 * idx + 1);
            tree.emplace(idx);
            if (lazy.count(2 * idx) && lazy.count(2 * idx + 1)) {
                lazy.emplace(idx);
            }
        }
    }

    bool book(int start, int end) {
        if (query(start, end - 1, 0, 1e9, 1)) {
            return false;
        }
        update(start, end - 1, 0, 1e9, 1);
        return true;
    }
};

或者:

class MyCalendarTwo {
public:
    MyCalendarTwo() {

    }
    void update(int s, int e, int val, int l, int r, int idx) {
        if (r < s || l > e) return;
        if (s <= l && r <= e) {
            tree[idx].first += val;
            tree[idx].second += val;
        } else {
            int mid = (l + r) >> 1;
            update(s, e, val, l, mid, 2 * idx);
            update(s, e, val, mid + 1, r, 2 * idx + 1);
            tree[idx].first = tree[idx].second + max(tree[2 * idx].first, tree[2 * idx + 1].first);
        }
    }    
    bool book(int start, int end) {
        update(start, end - 1, 1, 0, 1e9, 1);
        if (tree[1].first > 2) {
            update(start, end - 1, -1, 0, 1e9, 1);
            return false;
        }
        return true;
    }
private:
    unordered_map<int, pair<int, int>> tree;
};

复杂度分析:

  • 时间复杂度: O ( n log ⁡ C ) O(n \log C) O(nlogC) ,其中 n n n 为日程安排的数量。由于使用了线段树查询,线段树的最大深度为 log ⁡ C \log C logC ,每次最多会查询 log ⁡ C \log C logC 个节点,每次求预定需时间复杂度为 O ( log ⁡ C + log ⁡ C ) O(\log C + \log C) O(logC+logC) ,因此时间复杂度为 O ( n log ⁡ C ) O(n \log C) O(nlogC) ,在此 C C C 取固定值即为 1 0 9 10^9 109
  • 空间复杂度: O ( n log ⁡ C ) O(n \log C) O(nlogC) ,其中 n n n 为日程安排的数量。由于该解法采用的为动态线段树,线段树的最大深度为 log ⁡ C \log C logC ,每次预定最多会在线段树上增加 log ⁡ C \log C logC 个节点,因此空间复杂度为 O ( n log ⁡ C ) O(n \log C) O(nlogC) ,在此 C C C 取固定值即为 1 0 9 10^9 109

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/997481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Single View Point Omnidirectional Camera Calibration from Planar Grids

参考论文 paper里面最关键的部分为这段 实际相当于有一个xi的参数要使用, 和正常的相机内参定义不太一样, 那么在3d往相机上投的时候, 是这样的 也参考: https://github.com/autonomousvision/kitti360Scripts/blob/master/kitti360scripts/helpers/project.py#L174-L198

TDesign数据请求wx.request

wxml页面如下&#xff1a; bind:tap绑定了handleAjax事件&#xff0c;js页面如下&#xff1a; 点击按钮&#xff0c;运行成功输出data 报错&#xff1a; 小程序 (qq.com)https://mp.weixin.qq.com/ 再次点击按钮ajax 运行成功&#xff1a;

JTAG 简介

文章目录 1、JTAG 基本原理1.1、JTAG接口包括以下几个信号&#xff1a;1.2、The Debug TAP State Machine (DBGTAPSM) 2、JTAG 的应用 1、JTAG 基本原理 JTAG是Joint Test Action Group的缩写&#xff0c;它是一种国际标准测试协议&#xff0c;主要用于芯片或印制电路板的边界…

SpringBoot系列(12):SpringBoot集成log4j2日志配置

最近项目上有使用到log4j2日志模板配置&#xff0c;本文简单总结一下之前的学习笔记&#xff0c;如有纰漏之处&#xff0c;请批评指正。 1. log4j2日志依赖 使用log4j2日志模板时&#xff0c;需要引入相关依赖&#xff0c;下边的两种依赖方式均可。 1.1 使用sl4j依赖时 <…

kafka学习-消费者

目录 1、消费者、消费组 2、心跳机制 3、消费者常见参数配置 4、订阅 5、反序列化 基本概念 自定义反序列化器 6、位移提交 6.1、自动提交 6.2、手动提交 同步提交 异步提交 7、再均衡 7.1、定义与基本概念 7.2、缺陷 7.3、如何避免再均衡 7.4、如何进行组内分…

React+Typescript+react-router 6 创建路由操作

本文我们来看看路由的安装 其实路由的操作没有什么变化 但是还是给大家讲一下 那么我们打开项目 在项目终端输入 npm install --save react-router react-router-dom安装 一下 react-router 和 react-router-dom 这都是react开发很基本的插件了 不过大家安装前先注意好我的版…

C++ 进制转化入门知识(1)

一、什么是进制 进制是一种用来表示数值的系统或方法&#xff0c;它是基于一个特定的基数来工作的。在我们常见的几种进制中&#xff0c;有&#xff1a; 1. **二进制&#xff08;基数 2&#xff09;**&#xff1a; 二进制只用两个数字&#xff1a;0和1。这是计算机内部使用…

GB28181学习(三)——心跳保活

心跳保活 要求&#xff1a; 1. 当原设备发现工作异常时&#xff0c;应立即向本SIP监控域的SIP服务器发送状态信息&#xff1b; 2. 无异常时&#xff0c;定时向本SIP监控域的SIP服务器发送状态信息&#xff1b; 3. 状态信息报送采用**MESSGAE**方法&#xff1b; 4. SIP设备宜在…

不同温度与工况的放电曲线与内阻曲线

在电动汽车中&#xff0c;机器学习被广泛应用于许多领域&#xff0c;包括电池状态估计。电池的状态 of charge (SOC) 是电池中可用能量的百分比。准确估计SOC对于优化电池性能、延长电池寿命和维护安全性至关重要。然而&#xff0c;SOC估计是一个复杂的任务&#xff0c;因为电池…

线性规划对偶问题:理论推导和实际应用

文章目录 对偶问题实例对偶问题定义和性质定义性质 对偶问题应用影子价格理论应用 参考文献 对偶问题实例 之前在很多地方&#xff0c;都看到过“对偶”这两个字眼&#xff0c;总觉得这个词很高大上。对偶理论的百度百科中甚至写到&#xff1a;“在线性规划早期发展中最重要的…

Unity之创建第一个2D游戏项目

一 Unity环境配置 1.1 Untity资源官网下载&#xff1a;https://unity.cn/releases 1.2 Unity Hub集成环境&#xff0c;包含工具和项目的管理 1.3 Unity Editor编辑器 1.4 Visual Studio 2022脚本编辑器 1.5 AndroidSKD&#xff0c;JDK&#xff0c;NDK工具&#xff0c;用于and…

tcp连接+套接字编程

tcp头部 tcp端口号 TCP的连接是需要四个要素确定唯一一个连接&#xff1a;&#xff08;源IP&#xff0c;源端口号&#xff09; &#xff08;目地IP&#xff0c;目的端口号&#xff09; 所以TCP首部预留了两个16位作为端口号的存储&#xff0c;而IP地址由上一层IP协议负责传递 源…

autoware.ai感知随笔--地面滤波

autwoware.ai中点云预处理–points_preprocessor points_preprocessor cloud_transformer: 点云坐标转换,将输入的点云转化为velodyne坐标系下的点云。 compare_map_filter: 对比激光雷达点云和点云地图&#xff0c;然后提取&#xff08;或去除&#xff09;一致的点。 |input_…

联通面试题

一、GC 1.1、目标 GC的主要作用是自动识别和释放不再使用的对象&#xff0c;回收其所占用的内存&#xff0c;以防止内存泄漏和内存溢出的问题。 1.2、如何实现 1.2.1、标记阶段 GC从根对象&#xff08;如线程栈中的引用、静态变量等&#xff09;开始&#xff0c;通过可达性…

CnosDB 签约京清能源,助力分布式光伏发电解决监测系统难题。

近日&#xff0c;京清能源采购CnosDB&#xff0c;升级其“太阳能光伏电站一体化监控平台”。该平台可以实现电站设备统一运行监控&#xff0c;数据集中管理&#xff0c;为操作人员、维护人员、管理人员提供全面、便捷、差异化的数据和服务。 京清能源集团有限公司&#xff08;…

【LeetCode】35.复杂链表的复制

题目 请实现 copyRandomList 函数&#xff0c;复制一个复杂链表。在复杂链表中&#xff0c;每个节点除了有一个 next 指针指向下一个节点&#xff0c;还有一个 random 指针指向链表中的任意节点或者 null。 示例 1&#xff1a; 输入&#xff1a;head [[7,null],[13,0],[11,4]…

并发-Executor框架笔记

Executor框架 jdk5开始&#xff0c;把工作单元与执行机制分离开来&#xff0c;工作单元包括Runable和Callable&#xff0c;执行机制由Executor框架来提供。 Executor框架简介 Executor框架的两级调度模型 Java线程被一对一映射为本地操作系统线程 java线程启动会创建一个本…

Linux单列模式实现线程池

目录 一、单列模式 1.1 单列模式概念以及实现条件 1.2 饿汉模式 1.1.1 饿汉模式代码实现 1.1.2 饿汉模式特征和优缺点 1.3 懒汉模式 1.3.1 懒汉模式代码实现 1.3.2 懒汉模式特征以及优缺点 二、线程池 2.1 线程池概念 2.2 实现简单线程池逻辑 2.3 模拟实现懒汉模式线程…

【八大经典排序算法】:直接插入排序、希尔排序实现 ---> 性能大比拼!!!

【八大经典排序算法】&#xff1a;直接插入排序、希尔排序实现 ---> 性能大比拼&#xff01;&#xff01;&#xff01; 一、 直接插入排序1.1 插入排序原理1.2 代码实现1.3 直接插入排序特点总结 二、希尔排序 ( 缩小增量排序 )2.1 希尔排序原理2.2 代码实现2.3 希尔排序特点…

UE5、CesiumForUnreal实现瓦片坐标信息图层效果

文章目录 1.实现目标2.实现过程2.1 原理简介2.2 cesium-native改造2.3 CesiumForUnreal改造2.4 运行测试3.参考资料1.实现目标 参考CesiumJs的TileCoordinatesImageryProvider,在CesiumForUnreal中也实现瓦片坐标信息图层的效果,便于后面在调试地形和影像瓦片的加载调度等过…