Files
hello-algo/ru/docs/chapter_heap/top_k.md
Yudong Jin 22b3b568ef fix Ru translation (#1894)
* docs(ru): replace prose quotes with guillemets

* docs(ru): replace prose semicolons with periods

* docs(ru): align animation title forms

* docs(ru): align figure and table references
2026-04-14 18:10:12 +08:00

74 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Задача Top-k
!!! question
Дан неупорядоченный массив `nums` длины $n$ . Требуется вернуть наибольшие $k$ элементов массива.
Для этой задачи мы сначала покажем два относительно прямолинейных способа решения, а затем более эффективный способ на основе кучи.
## Метод 1: выбор через обход
Как показано на рисунке ниже, можно выполнить $k$ проходов по массиву и на каждом проходе извлекать соответственно $1$-й, $2$-й, $\dots$ , $k$-й по величине элемент. Временная сложность такого подхода равна $O(nk)$ .
Этот метод подходит только для случая $k \ll n$ , потому что когда $k$ приближается к $n$ , его временная сложность стремится к $O(n^2)$ , а это уже очень затратно.
![Поиск наибольших k элементов через обход](top_k.assets/top_k_traversal.png)
!!! tip
Когда $k = n$ , мы получаем полную упорядоченную последовательность, и в этот момент задача становится эквивалентной алгоритму «сортировка выбором».
## Метод 2: сортировка
Как показано на рисунке ниже, можно сначала отсортировать массив `nums` , а затем вернуть его крайние правые $k$ элементов. Временная сложность такого метода равна $O(n \log n)$ .
Очевидно, что этот способ делает слишком много, потому что нам нужно только найти наибольшие $k$ элементов, а сортировать остальные элементы совсем не обязательно.
![Поиск наибольших k элементов через сортировку](top_k.assets/top_k_sorting.png)
## Метод 3: куча
Задачу Top-k можно решить гораздо эффективнее с помощью кучи, как показано на рисунке ниже.
1. Инициализировать минимальную кучу, у которой вершина содержит наименьший элемент.
2. Сначала по очереди поместить в кучу первые $k$ элементов массива.
3. Начиная с элемента номер $k + 1$ , если текущий элемент больше элемента на вершине кучи, то извлечь вершину кучи и поместить в кучу текущий элемент.
4. После завершения обхода в куче будут храниться как раз наибольшие $k$ элементов.
=== "<1>"
![Поиск наибольших k элементов с помощью кучи](top_k.assets/top_k_heap_step1.png)
=== "<2>"
![top_k_heap_step2](top_k.assets/top_k_heap_step2.png)
=== "<3>"
![top_k_heap_step3](top_k.assets/top_k_heap_step3.png)
=== "<4>"
![top_k_heap_step4](top_k.assets/top_k_heap_step4.png)
=== "<5>"
![top_k_heap_step5](top_k.assets/top_k_heap_step5.png)
=== "<6>"
![top_k_heap_step6](top_k.assets/top_k_heap_step6.png)
=== "<7>"
![top_k_heap_step7](top_k.assets/top_k_heap_step7.png)
=== "<8>"
![top_k_heap_step8](top_k.assets/top_k_heap_step8.png)
=== "<9>"
![top_k_heap_step9](top_k.assets/top_k_heap_step9.png)
Пример кода приведен ниже:
```src
[file]{top_k}-[class]{}-[func]{top_k_heap}
```
Всего выполняется $n$ операций добавления и извлечения из кучи, а максимальная длина кучи равна $k$ , поэтому временная сложность равна $O(n \log k)$ . Этот метод очень эффективен: когда $k$ мало, временная сложность стремится к $O(n)$. Когда $k$ велико, она все равно не превышает $O(n \log n)$ .
Кроме того, этот метод подходит и для сценариев с динамическим потоком данных. При непрерывном поступлении новых данных мы можем продолжать поддерживать содержимое кучи, тем самым динамически обновляя наибольшие $k$ элементов.