Files
hello-algo/ru/docs/chapter_heap/top_k.md
2026-01-20 15:08:42 +08:00

4.7 KiB
Raw Permalink Blame History

Задача Top-k

!!! question

Дан неупорядоченный массив `nums` длиной $n$. Необходимо вернуть $k$ наибольших элементов массива.

Для решения этой задачи сначала рассмотрим два метода с более прямолинейным подходом, а затем более эффективное решение с использованием кучи.

Метод 1: обход с выбором

Можно выполнить k раундов обхода, как показано на рисунке ниже, извлекая в каждом раунде 1-й, 2-й, \dots, $k$-й по величине элемент. Временная сложность составляет O(nk).

Этот метод подходит только для случаев, когда k \ll n, поскольку при k, близком к n, временная сложность приближается к O(n^2), что очень затратно по времени.

Обход для поиска k наибольших элементов

!!! tip

При $k = n$ мы получаем полную упорядоченную последовательность, что эквивалентно алгоритму «сортировка выбором».

Метод 2: сортировка

Как показано на рисунке ниже, можно сначала отсортировать массив nums, а затем вернуть k крайних правых элементов. Временная сложность составляет O(n \log n).

Очевидно, что этот метод выполняет задачу «с избытком», поскольку нам нужно найти только k наибольших элементов, а не сортировать остальные элементы.

Сортировка для поиска k наибольших элементов

Метод 3: куча

Задачу Top-k можно решить более эффективно с использованием кучи. Процесс показан на рисунке ниже.

  1. Инициализируется минимальная куча, элемент на вершине которой является наименьшим.
  2. Сначала первые k элементов массива последовательно добавляются в кучу.
  3. Начиная с элемента k + 1, если текущий элемент больше элемента на вершине кучи, элемент с вершины извлекается, а текущий элемент добавляется в кучу.
  4. После завершения обхода в куче сохраняются k наибольших элементов.

=== "<1>" Поиск k наибольших элементов с использованием кучи

=== "<2>" top_k_heap_step2

=== "<3>" top_k_heap_step3

=== "<4>" top_k_heap_step4

=== "<5>" top_k_heap_step5

=== "<6>" top_k_heap_step6

=== "<7>" top_k_heap_step7

=== "<8>" top_k_heap_step8

=== "<9>" top_k_heap_step9

Пример кода приведен ниже:

[file]{top_k}-[class]{}-[func]{top_k_heap}

Всего выполняется n раундов добавления и извлечения элементов, максимальная длина кучи равна k, поэтому временная сложность составляет O(n \log k). Этот метод очень эффективен: при малых k временная сложность стремится к O(n); при больших k временная сложность не превышает O(n \log n).

Кроме того, этот метод подходит для сценариев использования с динамическими потоками данных. При непрерывном добавлении данных можно постоянно поддерживать элементы в куче, обеспечивая динамическое обновление k наибольших элементов.