Четыре базовых способа хранить последовательность. Отличаются не тем, что умеют, а тем, что у них дёшево, а что дорого.
Ключевые тезисы
- Массив даёт доступ по индексу за константу, но вставка в середину линейна.
- наоборот: вставка за константу, доступ по индексу — за линию.
- и очередь — не структуры, а дисциплины доступа: LIFO и FIFO поверх массива или списка.
- Поверх этих дисциплин строится отдельный приём — и дек: ближайший больший элемент и максимум в окне за один проход.
Какую задачу решает
Массив, список, и очередь хранят одно и то же — последовательность элементов. Выбор между ними никогда не про то, что структура «умеет»: умеют все всё. Выбор про цену: какая операция стоит константу, а какая — проход по всей длине.
и очередь стоят в этом ряду особняком. Это не структуры, а дисциплины доступа — правило, в каком порядке элементы забираются обратно. Стек отдаёт последний положенный и потому точно повторяет устройство вложенности: скобки, вызовы функций, отмена действий. Очередь отдаёт первый и задаёт обработку по слоям, на которой держится .
Отдельного разговора заслуживают два приёма поверх этих дисциплин — и дек. Первый за один проход находит для каждой позиции ближайший больший элемент, второй даёт максимум в без логарифма. Оба выглядят как трюки и оба на самом деле следуют из одного наблюдения: элемент, который уже никогда не станет ответом, можно выбросить сразу.
Есть ряд ежедневных значений метрики, и для каждого дня нужно знать, через сколько дней она впервые превысила текущий уровень. Наивно — для каждого дня идти вперёд до первого большего: на ровном ряде это квадрат. переворачивает задачу: в стеке лежат дни, для которых ответ ещё не найден, и значения по ним убывают. Приходит новый день — он сразу закрывает все дни в стеке, где значение меньше, и каждый из них снимается навсегда. Каждый день кладётся один раз и снимается один раз, поэтому вложенный while внутри цикла не делает решение квадратичным — и это ровно то место, которое стоит проговорить вслух на собеседовании.
Подробный разбор
Выбор между массивом и списком — не вопрос вкуса и не вопрос того, что структура «умеет». Обе хранят последовательность и обе позволяют всё. Разница в том, какая операция стоит константу, а какая — проход по всей длине.
| Операция | Массив | Дек | |
|---|---|---|---|
| Доступ по индексу | |||
| Вставка в конец | амортизированно | ||
| Вставка в начало | |||
| Вставка в середину | при известном узле | ||
| Память на элемент | только значение | значение и ссылки | значение |
| Обход | последовательный, дружит с кешем | прыжки по памяти | последовательный |
Таблица честна асимптотически и обманчива практически. Обход массива на порядок быстрее обхода списка той же длины, потому что процессор читает память страницами. Список выигрывает там, где узлы уже на руках и нужно много вставок в середину, — и это заметно более редкий случай, чем кажется.
— не структура, а дисциплина доступа: положили сверху, сняли сверху. Она в точности повторяет устройство вложенности, поэтому любая задача, где что-то открывается и должно закрыться в обратном порядке, решается стеком.
символ действие стек
( кладём (
[ кладём ( [
{ кладём ( [ {
} вершина { -- парная, снимаем ( [
] вершина [ -- парная, снимаем (
) вершина ( -- парная, снимаем пусто
стек пуст в конце -> последовательность правильная- Скобки и теги — проверка вложенности в выражениях, JSON, XML, HTML.
- Вычисление выражений — обратная польская запись и алгоритм сортировочной станции.
- Отмена действий — история правок, где откатывается последнее.
- Рекурсия без рекурсии — обход дерева явным стеком, когда глубина грозит переполнить системный.
- Упрощение путей —
a/b/../c— это каталогов.
и вызов функции — одно и то же: возврат всегда происходит к последнему вызвавшему. Поэтому любая механически переписывается в цикл со стеком, и в этом смысле рекурсия — просто синтаксис поверх этой структуры.
Два приёма, построенных на стеке и деке, разобраны отдельно: они решают не задачу хранения, а задачу поиска. за один проход находит для каждой позиции ближайший больший элемент, монотонный дек — максимум внутри .
Разбор целиком — в теме монотонный стек и дек: трассировка по шагам, инвариант, четыре комбинации «больший или меньший, слева или справа» и типичные ошибки.
| Сигнал в условии | Структура |
|---|---|
| Вложенность, парность, «последнее открытое» | |
| «Ближайший больший (меньший) справа или слева» | |
| «Сколько ждать до следующего большего» | |
| Максимум или минимум внутри | дек |
| Обработка в порядке поступления, «по слоям» | |
| Нужен текущий минимум при добавлениях и удалениях | |
| Частые вставки в начало | дек или список, но не массив |
- Наивное решение звучит как «для каждого элемента ищем ближайший подходящий» — это почти всегда .
- Требуется и порядок, и быстрый доступ по значению — одной структуры мало, берут связку словаря и списка (так устроен LRU-кеш).
- Нужен k-й по величине, а не крайний — это уже или порядковые статистики, а не .
Быстрая проверка: если в решении хочется «вернуться назад и посмотреть, что было», спросите, в каком порядке вы будете туда возвращаться. В обратном — , в прямом — очередь, с обеих сторон — дек.
Где применяется
- Разбор вложенных структурСкобки, теги, выражения, пути — везде .
- Ближайший больший элементТемпературы, биржевые диапазоны, прямоугольник в гистограмме — .
- задаёт порядок «сначала ближние, потом дальние».
- Потоковая обработкаКольцевой буфер и скользящий максимум по последним k событиям.
Плюсы, минусы и альтернативы
Плюсы
- Операции за константу: и , и очередь, и дек не зависят от длины.
- снимает целый порядок сложности в задачах «ближайший подходящий».
- Реализуются поверх массива, поэтому дружат с кешем и не требуют ссылок на каждый элемент.
Минусы
- Ни одна из дисциплин не даёт доступа в середину — за ним идут к другим структурам.
- отвечает на один вопрос: ближайший, а не k-й и не любой подходящий.
- привлекателен асимптотически и почти всегда проигрывает массиву на практике.
Брать, если
- В условии есть вложенность, парность или откат к последнему открытому.
- Для каждого элемента нужен ближайший больший или меньший.
- Нужен максимум или минимум внутри .
Не брать, если
- Нужен k-й по величине или текущий минимум при произвольных удалениях — это .
- Нужен доступ по значению — это .
- Нужен порядок и диапазонные запросы одновременно — это дерево поиска.
Чем заменяют
Видеолекции
Записи университетских курсов, где эта тема звучит. Где у записи есть тайм-коды, ссылка открывает её с нужной секунды; где их не проставили — с начала.
Связанные темы
Структуры данных
Hash Tables85%
Хеш-таблицы · Алгоритмы и структуры данныхДоступ по ключу за константу в среднем. Основа словарей и множеств — и структура, на которой держится большая часть повседневного кода.
Trees and Heaps85%
Деревья и кучи · Алгоритмы и структуры данныхИерархические структуры, дающие логарифмические операции: двоичное дерево поиска для упорядоченных запросов, куча — для быстрого минимума.
Sorting Algorithms85%
Сортировки · Алгоритмы и структуры данныхБыстрая, слиянием, пирамидальная — три способа упорядочить данные за O(n log n) с разными свойствами по памяти, устойчивости и худшему случаю.
Order Statistics85%
Порядковые статистики · Алгоритмы и структуры данныхНайти k-й по величине элемент, не сортируя весь массив. Quickselect делает это в среднем за линию.
Caching85%
Кеширование · Системный дизайн ML-сервисовНе считать то, что уже посчитано: кеш предсказаний, признаков и эмбеддингов.
Всё, что названо в описании, разобрано здесь же или в соседней теме — переходы под каждым определением.
Цена операции
Не что структура умеет, а что у неё стоит константу, а что — линию.
Разбор ниже: Цена операции, а не набор возможностейСтек (LIFO)
Последний положенный забирается первым. Дисциплина вложенности.
Разбор ниже: Стек: последнее открытое закрывается первымОчередь (FIFO)
Первый положенный забирается первым. Обработка по слоям.
Разбор ниже: Цена операции, а не набор возможностейМонотонный стек и дек
Приём поверх этих структур: ближайший больший и максимум в окне за один проход.
Отдельная тема: Monotonic Stack and Deque — Монотонный стек и декТема встречается в тесте по направлениям — ошибки приведут обратно на эту страницу.
Глава «Алгоритмы и структуры данных» последний раз правилась . Нашли ошибку — напишите.