返回

我的日程安排表系列算法题解析

见解分享

我的日程安排表系列算法题解析

引言

《我的日程安排表》系列算法题是LeetCode上非常经典的题目,考察了数据结构和算法的基本功。这些题目难度适中,适合不同阶段的算法学习者。本文将对这三个题目进行详细解析,帮助读者深入理解它们。

729. 我的日程安排表 I

题目

给你一个时间范围,请你找出所有在这个时间范围内的事件。

思路分析:

这道题目可以用树状数组或者线段树来解决。树状数组是一种高效的数据结构,可以用于区间查询和单点更新。线段树是一种更加通用的数据结构,也可以用于区间查询和单点更新,但它的实现更加复杂。

代码实现(树状数组):

class MyCalendar {

    int[] tree;
    int n;

    public MyCalendar() {
        n = 1000000;
        tree = new int[n];
    }

    public boolean book(int start, int end) {
        if (query(end - 1) - query(start - 1) > 0) {
            return false;
        }
        update(start, 1);
        update(end, -1);
        return true;
    }

    private void update(int idx, int val) {
        while (idx < n) {
            tree[idx] += val;
            idx += lowbit(idx);
        }
    }

    private int query(int idx) {
        int sum = 0;
        while (idx > 0) {
            sum += tree[idx];
            idx -= lowbit(idx);
        }
        return sum;
    }

    private int lowbit(int x) {
        return x & (-x);
    }
}

时间复杂度:

  • book 操作:O(log(n))
  • query 操作:O(log(n))

空间复杂度:

O(n)

731. 我的日程安排表 II

题目:

给你一个时间范围,请你找出所有在这个时间范围内的重叠事件。

思路分析:

这道题目可以用线段树来解决。线段树是一种更加通用的数据结构,可以用于区间查询和单点更新。

代码实现:

class MyCalendarTwo {

    class SegmentTreeNode {
        int start, end;
        int count;
        int overlapCount;
        SegmentTreeNode left, right;

        public SegmentTreeNode(int start, int end) {
            this.start = start;
            this.end = end;
            this.count = 0;
            this.overlapCount = 0;
            this.left = null;
            this.right = null;
        }
    }

    SegmentTreeNode root;

    public MyCalendarTwo() {
        root = null;
    }

    public boolean book(int start, int end) {
        if (root == null) {
            root = new SegmentTreeNode(start, end);
            return true;
        }
        return insert(root, start, end);
    }

    private boolean insert(SegmentTreeNode node, int start, int end) {
        if (node.start == start && node.end == end) {
            node.count++;
            if (node.count == 1) {
                node.overlapCount = 0;
                return true;
            } else if (node.count == 2) {
                node.overlapCount = 1;
                return false;
            } else {
                return false;
            }
        }
        boolean result = true;
        int mid = (node.start + node.end) / 2;
        if (end <= mid) {
            if (node.left == null) {
                node.left = new SegmentTreeNode(node.start, mid);
            }
            result = insert(node.left, start, end);
        } else if (start > mid) {
            if (node.right == null) {
                node.right = new SegmentTreeNode(mid + 1, node.end);
            }
            result = insert(node.right, start, end);
        } else {
            if (node.left == null) {
                node.left = new SegmentTreeNode(node.start, mid);
            }
            if (node.right == null) {
                node.right = new SegmentTreeNode(mid + 1, node.end);
            }
            result = insert(node.left, start, mid) && insert(node.right, mid + 1, end);
        }
        node.count = node.left != null ? node.left.count : 0;
        node.count += node.right != null ? node.right.count : 0;
        node.overlapCount = node.left != null ? node.left.overlapCount : 0;
        node.overlapCount += node.right != null ? node.right.overlapCount : 0;
        if (node.count == 2 && node.overlapCount == 0) {
            node.overlapCount = 1;
            result = false;
        }
        return result;
    }
}

时间复杂度:

  • book 操作:O(log(n))

空间复杂度:

O(n)

732. 我的日程安排表 III

题目:

给你一个事件列表,其中每个事件都有一个开始时间和结束时间。请你找出可以在任何时间段内同时发生的事件数量的最大值。

思路分析:

这道题目可以用扫描线算法来解决。扫描线算法是一种用于解决区间重叠问题的算法。它将事件按时间顺序排序,然后从左到右扫描时间轴。在每个时间点,算法都会记录当前重叠的事件数。

代码实现:

class MyCalendarThree {

    TreeMap<Integer, Integer> map;

    public MyCalendarThree() {
        map = new TreeMap<>();
    }

    public int book(int start, int end) {
        map.put(start, map.getOrDefault(start, 0) + 1);
        map.put(end, map.getOrDefault(end, 0) - 1);
        int max = 0, sum = 0;
        for (int value : map.values()) {
            sum += value;
            max = Math.max(max, sum);
        }
        return max;
    }
}

时间复杂度:

  • book 操作:O(log(n))

空间复杂度:

O(n)

总结

《我的日程安排表》系列算法题考察了数据结构和算法的基本功。这三个题目难度适中,适合不同阶段的算法学习者。本文详细解析了这些题目,希望对读者有所帮助。