《剑指 Offer》专项突破版 - 面试题 58 : 日程表(C++ 实现)

题目链接LCR 058. 我的日程安排表 I - 力扣(LeetCode)

题目

请实现一个类型 MyCalendar 用来记录自己的日程安排,该类型用方法 book(int start, int end) 在日程表中添加一个时间区域为 [start, end) 的事项(这是一个左闭右开区间)。如果 [start, end) 中之前没有安排其他事项,则成功添加该事项并返回 true;否则,不能添加该事项,并返回 false。

例如,在下面的 3 次调用 book 方法中,第 2 次调用返回 false,这是因为时间 [15, 20) 已经被第 1 次调用预留了。由于第 1 次占用的时间是一个左闭右开区间,并没有真正占用时间 20,因此不影响第 3 次调用预留时间区间 [20, 30)。

MyCalendar cal = new MyCalendar();
cal.book(10, 20);  // return true
cal.book(15, 25);  // return false
cal.book(20, 30);  // return true

分析

添加到日程表中的每个事项都占用一个时间段。根据题目的要求,两个事项不能占用同一个时间段,也就是说,事项对应的时间区间不能重叠。当需要插入一个新的事项时,就需要遍历日程表中已有事项占用的时间区间。

如果待添加的事项占用的时间区间是 [m, n),就需要找出开始时间小于 m 的所有事项中开始最晚的一个,以及开始时间大于或等于 m 的所有事项中开始最早的一个。如果待添加的事项和这两个事项都没有重叠,那么该事项可以添加到日程表中

因此,需要高效地根据开始时间查找时间区间。可以根据时间区间的开始时间进行排序,通常排序之后能够优化查找的效率。如果用一个排序的动态数组(如 C++ 中的 vector)来保存日程表中的时间区间,那么查找的时间复杂度是 O(logn),但在排序数组中插入新的时间区间的时间复杂度是 O(n)。

也可以利用二叉搜索树来优化查找的效率。如果把时间区间存储到二叉搜索树中,那么在二叉搜索树中查找、插入和删除的时间复杂度都是 O(logn)。

C++ 提供了 set 和 map 这两种二叉搜索树的数据结构。由于每个时间区间都有开始时间和结束时间,也就是说,树的每个节点需要保存两个数字,所以使用 map。在 map 中,每个节点是一个映射,可以把时间区间的开始时间作为映射的键,把结束时间作为映射的值

代码实现

class MyCalendar {
public:
    bool book(int start, int end) {
        map<int, int>::iterator it = events.begin();
        while (it != events.end())
        {
            if (it->first < start)
            {
                if (it->second > start)
                    return false;
            }
            else
            {
                break;
            }
            ++it;
        }
​
        it = events.lower_bound(start);
        if (it != events.end() && it->first < end)
            return false;
​
        events[start] = end;
        return true;
    }
private:
    map<int, int> events;
};
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值