LeetCode 729. Мой календарь I [Дизайн; упорядоченный набор, двоичный поиск; дерево отрезков] Medium

Эта статья относится к серии статей «Покоряя LeetCode», официально стартовавшей 12.08.2021. Поскольку некоторые вопросы в LeetCode заблокированы, эта серия будет продолжаться, по крайней мере, до тех пор, пока все разблокированные вопросы не будут удалены; поскольку LeetCode все еще создает новые вопросы, дата окончания этой серии может быть навсегда. В этой серии статей о решении проблем я не только объясню различные идеи решения проблем и их оптимизацию, но также буду использовать различные языки программирования для решения проблем.Что касается общих методов решения, я буду также суммируйте соответствующие шаблоны алгоритмов.

Чтобы облегчить запуск, отладку и обмен файлами кода на ПК, я также создал соответствующие хранилища . На этом складе вы можете увидеть не только ссылку на исходный вопрос LeetCode, код решения, ссылку на статью решения, сводку похожих вопросов, сводку распространенных решений и т. д., но также важную информацию, такую ​​​​как частота исходных вопросов. и связанных с ними компаний. Если у вас есть другие предпочтительные решения, вы можете поделиться ими с другими.

Поскольку содержание этой серии статей может быть обновлено и изменено в любое время, вы можете подписаться на статью «Оглавление серии статей Conquering 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 одного раза.

Этот вопрос взят из: Еженедельного конкурса 59. Подобные вопросы, касающиеся интервальных моделей, включают:

Решение 1. Траверс напрямую

Мы записываем все забронированные интервалы расписания курса, а когда бронируем новый интервал [start, end) [\textit{start}, \textit{end})[ начало ,end ) , в это время проверьте, не конфликтует ли каждое забронированное в данный момент расписание с новым расписанием. Если конфликта нет, можно добавить новое расписание.

  • Для двух интервалов [s 1 , e 1 ) [s_1, e_1)[ с1,е1) сумма[ s 2 , e 2 ) [s_2, e_2)[ с2,е2) , если между ними нет пересечения, тоs 1 ≥ e 2 s_1 \ge e_2с1е2s 2 ≥ e 1 s_2 \ge e_1с2е1, что означает, что если s 1 < e 2 s_1 < e_2с1<е2и s 2 < e 1 s_2 < e_1с2<е1
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)О ( н2 ), гдеnnn представляет количество расписаний. Потому что каждый раз, когда вы бронируете билет, вам необходимо просмотреть все забронированные маршруты.
  • Пространственная сложность: O ( n ) O(n)O ( n ) , из нихnnn представляет количество расписаний. Все забронированные поездки необходимо сохранять.

Решение 2. Бинарный поиск

Если мы поддерживаем расписания в хронологическом порядке, мы можем проверить, может ли новое расписание быть забронировано с помощью двоичного поиска ситуации с расписанием , и если да, то обновить вставленное расписание в структуру сортировки.

Если вам нужна структура данных, которая может сортировать элементы и поддерживать быструю вставку , вы можете использовать TreeSet \texttt{TreeSet}TreeSet для сборки. Для заданного интервала[начало, конец) [начало,конец)[ начало , _ _ _ _e n d ) каждый раз, когда мы обнаруживаем, что начальная точка больше или равнаend \textit{end}Первый интервал конца [l 1, r 1) [l_1,r_1)[ л1,р1) , а рядом с[ l 1 , r 1 ) [l_1,r_1)[ л1,р1) есть[ l 2 , r 2 ) [l_2,r_2)[ л2,р2) , еслиr 2 ≤ start < end ≤ l 1 r_2 \le \textit{start} < \textit{end} \le l_1р2начинать<конецл1, то интервал можно забронировать.

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)О ( нло гn ) , гдеnnn представляет количество расписаний. Поскольку каждый раз при резервировании требуется бинарный поиск, а требуемое время равноO ( log ⁡ n ) O(\log n)О ( log гп )
  • Пространственная сложность: O ( n ) O(n)O ( n ) , из нихnnn представляет количество расписаний. Все забронированные поездки необходимо сохранять.

Решение 3. Дерево отрезков линии

Предположим, используя дерево отрезков, мы открываем массив arr [ 0 , ⋯ , 1 0 9 ] \textit{arr}[0,\cdots, 10^9]обр [ 0 ,,1 09 ]первоначально значение каждого элемента равно0 00 , для каждого интервала бронирования поездки[начало, конец) [начало, конец)[ начало , _ _ _ _e n d ) , затем меняем элементы в интервалеarr [ start , ⋯ , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1]прибытие [ начало ,,конец1 ] каждый элемент помечен1 11 , каждый раз, когдакнига \texttt{book}book , нам нужно только обнаружитьarr [ start , ⋯ , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1]прибытие [ начало ,,конец1 ] Есть ли какой-либо элемент в интервале, отмеченном как1 11 . На самом деле нам не обязательно открывать массивarr \textit{arr}arr , можно использовать динамическое дерево отрезков линий, lazy marklazy \textit{lazy}интервал ленивой отметки [ l , r ] [l,r][ л ,r ] уже забронировано,дерево \textit{tree}интервал записи дерева [l, r] [l,r][ л ,Существование r ] отмечается как 1 11 элемент.

Каждый раз книга \texttt{book}При работе с книгой сначала определите интервал [start, ⋯, end − 1] [\textit{start},\cdots,\textit{end}-1][ начало ,,конец1 ] Есть ли элемент, который нужно отметить, если он существует, он помечен как1 1Элемент 1 указывает, что интервал не может быть забронирован; в противном случае его можно зарезервировать. После завершения резервированияarr [ start , ⋯ , end − 1 ] \textit{arr}[\textit{start},\cdots,\textit{end}-1]прибытие [ начало ,,конец1 ] отмечен как1 11 и одновременно обновите дерево сегментов.

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)О ( нло гC ) , гдеnnn — количество расписаний. Благодаря использованию запросов к дереву сегментов максимальная глубина дерева сегментов равнаlog ⁡ C \log C.ло гC , не более log ⁡ C \log Cбудет запрашиваться каждый разло гИмеется C узлов, и временная сложность, необходимая для каждого поиска, равнаO ( log ⁡ C + log ⁡ C ) O(\log C + \log C)О ( log гС+ло гC ) , поэтому временная сложность равнаO ( n log ⁡ C ) O(n \ log C)О ( нло гC ) , здесьCCC принимает фиксированное значение, равное1 0 9 10^9.1 09
  • Пространственная сложность: O ( n log ⁡ C ) O(n \ log C)О ( нло гC ) , гдеnnn — количество расписаний. Поскольку в этом решении используется динамическое дерево сегментов, максимальная глубина дерева сегментов равнаlog ⁡ C \log C.ло гC каждое резервирование добавит не более log ⁡ C \log Cк дереву сегментов.ло гC узлов, поэтому пространственная сложность равнаO ( n log ⁡ C ) O(n \ log C)О ( нло гC ) , здесьCCC принимает фиксированное значение, равное1 0 9 10^9.1 09

Supongo que te gusta

Origin blog.csdn.net/myRealization/article/details/132795135
Recomendado
Clasificación