力扣 729. 我的日程安排表 I

题目来源:https://leetcode.cn/problems/my-calendar-i/

大致题意:
设置一个 MyCalendar 类,包含以下功能:

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

思路

使用线段树存下日程,每添加一次日程,将日程对应的区间在线段树中做标记。每次添加前,先检查当前日程区间内是否有被添加过的时间段,若有则不添加该日程。

因为线段树使用节点数目约是节点范围的 4 倍,而本体节点范围为 [0, 109],范围过大,不适合直接申请空间。所以采用了动态开点的方法,每放入一个日程,动态创建与日程区间相关的线段树节点。并使用懒标记标注当前节点所覆盖区间是否都已经安排过日程。

具体添加日程的操作如下:

  1. 先判断日程对应区间内是否有被添加过,查询时可以通过懒标记和查询区间内是否已经创建过节点判断:若有懒标记或者查询区间内有节点被创建,则该区间一定有被添加过的时间段
  2. 若整个区间没有被插入过,则动态开点插入该日程

代码:

public class MyCalendar_SegmentTree {
    Set<Integer> tree;  // 表示节点范围内有被预定的区间
    Set<Integer> lazy;  // 表示节点范围内都被预定
    int BORDER; // 节点最大范围

    public MyCalendar_SegmentTree() {
        tree = new HashSet<>();
        lazy = new HashSet<>();
        BORDER = (int) 1e9;
    }

    public boolean book(int start, int end) {
        // 首先查询当前日程区间内是否有被插入过
        if (query(start, end - 1, 0, BORDER, 1)) {
            return false;
        }
        // 插入当前日程
        update(start, end - 1, 0, BORDER, 1);
        return true;
    }

    /**
     * 查询 [start, end] 区间是否有线段树节点,若有则表示该区间内有被预定的部分
     *
     * @param start
     * @param end
     * @param l     当前线段树节点的左边界
     * @param r     当前线段树节点的右边界
     * @param idx   当前线段树节点编号
     * @return
     */
    public boolean query(int start, int end, int l, int r, int idx) {
        // 当前线段树节点不包含查询范围
        if (r < start || end < l) {
            return false;
        }
        // 若节点范围都被预定过,直接返回 true
        if (lazy.contains(idx)) {
            return true;
        }
        // 当前线段树节点的范围都在查询范围内
        if (start <= l && r <= end) {
            // 那么只要线段树节点存在,就表示该线段树节点范围被预定过
            return tree.contains(idx);
        }
        int mid = (l + r) >> 1;
        // 查询范围只在线段树左节点中
        if (end <= mid) {
            return query(start, end, l, mid, idx * 2);
        } else if (mid < start) { // 查询范围只在线段树右节点中
            return query(start, end, mid + 1, r, idx * 2 + 1);
        } else {    // 查询范围横跨线段树两个子节点
            return query(start, end, l, mid, idx * 2) || query(start, end, mid + 1, r, idx * 2 + 1);
        }
    }

    /**
     * 预定区间 [start, end]
     *
     * @param start
     * @param end
     * @param l     当前线段树节点左边界
     * @param r     当前线段树节点右边界
     * @param idx   当前线段树节点编号
     */
    public void update(int start, int end, int l, int r, int idx) {
        if (r < start || end < l) {
            return;
        }
        // 当前线段树节点的范围都在更新范围内
        if (start <= l && r <= end) {
            tree.add(idx);
            lazy.add(idx);
        } else {
            int mid = (l + r) >> 1;
            if (mid >= end) {   // 更新范围只在线段树左节点
                update(start, end, l, mid, idx * 2);
            } else if (mid < start) {   // 更新范围只在线段树右节点
                update(start, end, mid + 1, r, idx * 2 + 1);
            } else {    // 更新范围横跨线段树左右节点
                update(start, end, l, mid, idx * 2);
                update(start, end, mid + 1, r, idx * 2 + 1);
            }
            // 当前线段树节点范围中有区间被预定
            tree.add(idx);
        }
    }

    public static void main(String[] args) {
        MyCalendar_SegmentTree tree = new MyCalendar_SegmentTree();
        System.out.println(tree.book(10, 20));
        System.out.println(tree.book(15, 25));
        System.out.println(tree.book(20, 30));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值