Можно ли отсортировать быстрее, чем за O(n log n)?
- Почему сортировка сравнением не может быть быстрее n log n?
- Что такое сортировка подсчётом и поразрядная сортировка?
Сравнениями — нельзя: любой алгоритм, который только сравнивает элементы, в худшем случае делает Ω(n log n) сравнений. Без сравнений — можно при ограничениях на ключи.
- Нижняя граница: n! перестановок требуют дерево решений глубины log₂(n!) ≈ n log n
- Сортировка подсчётом — O(n + k) для целых в небольшом диапазоне k
А ещё Поразрядная (radix) — O(d·(n + k)) по цифрам; блочная (bucket) — для равномерно распределённых данных
Подробно
Почему n log n — предел для сравнений. Алгоритм сортировки сравнением можно представить как дерево решений: в каждом узле — сравнение двух элементов, в каждом листе — конкретная перестановка, которая сортирует вход. Различных входов-перестановок n!, и каждая должна вести в свой лист. Двоичное дерево с n! листьями имеет высоту не меньше log₂(n!), а это порядка n log n (по формуле Стирлинга). Высота — число сравнений в худшем случае. Значит, слияние и пирамидальная сортировка асимптотически оптимальны.
Как обойти ограничение — не сравнивать, а использовать структуру ключей:
- сортировка подсчётом — для целых чисел в диапазоне 0..k: считаем, сколько раз встречается каждое значение, затем выписываем по порядку. O(n + k) времени и O(k) памяти. Выгодна, когда k не больше порядка n (оценки, возраст, байты);
- поразрядная (radix) — сортируем устойчиво по каждой цифре, от младшей к старшей, сортировкой подсчётом. O(d·(n + k)), где d — число разрядов. Подходит для целых, строк фиксированной длины, IP-адресов;
- блочная (bucket) — раскладываем по корзинам по значению и сортируем корзины; линейная в среднем для равномерно распределённых чисел.
Ограничения: нужна информация о ключах (целые, диапазон, длина), много памяти при большом k; для произвольных объектов с компаратором остаётся O(n log n).
Пример
def counting_sort(a, max_value):
counts = [0] * (max_value + 1)
for x in a:
counts[x] += 1
return [v for v, c in enumerate(counts) for _ in range(c)]
counting_sort([3, 1, 4, 1, 5, 2], 5) # [1, 1, 2, 3, 4, 5]Что хотят услышать
- Ответ: сравнениями нельзя, без сравнений — можно при ограничениях.
- Идея доказательства через дерево решений и n!.
- Сортировка подсчётом и поразрядная, их сложность и условия применимости.
Частые ошибки
- Утверждать, что сортировка подсчётом «всегда лучше» — при большом диапазоне она расточительна.
- Не знать, откуда берётся оценка n log n.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Любую сортировку можно сделать за O(n)
- ✗Нельзя никак: нижняя граница n log n доказана для всех алгоритмов сортировки, включая сортировку подсчётом и поразрядную, при любых ключах
- ✓Сортировка сравнениями не быстрее Ω(n log n) в худшем случае; без сравнений, при ограничениях на ключи (подсчёт, radix), — быстрее
- ✗Сортировка подсчётом работает для любых ключей
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.