This commit is contained in:
krahets
2026-04-14 18:06:14 +08:00
parent 065a978848
commit e53a7f2498
93 changed files with 565 additions and 570 deletions
+4 -4
View File
@@ -12,13 +12,13 @@ comments: true
Каждый раз, когда элемент добавляется в кучу, ее длина увеличивается на единицу. Поскольку узлы последовательно добавляются в двоичное дерево сверху вниз, куча строится сверху вниз.
Пусть число элементов равно $n$ ; так как каждая операция добавления требует $O(\log{n})$ времени, временная сложность такого построения кучи составляет $O(n \log n)$ .
Пусть число элементов равно $n$. Так как каждая операция добавления требует $O(\log{n})$ времени, временная сложность такого построения кучи составляет $O(n \log n)$ .
## 8.2.2   Реализация через обход и упорядочивание
На самом деле можно реализовать и более эффективный способ построения кучи, который состоит из двух шагов.
1. Без изменений добавить все элементы списка в кучу; в этот момент свойства кучи еще не выполняются.
1. Без изменений добавить все элементы списка в кучу. В этот момент свойства кучи еще не выполняются.
2. Обойти кучу в обратном порядке, то есть в порядке, обратном обходу по уровням, и по очереди выполнить упорядочивание сверху вниз для каждого нелистового узла.
**После того как некоторый узел был упорядочен, поддерево с этим узлом в качестве корня становится корректной подкучей**. А поскольку обход выполняется в обратном порядке, куча строится снизу вверх.
@@ -336,13 +336,13 @@ comments: true
Перемножив эти два значения, можно получить временную сложность построения кучи $O(n \log n)$ . **Но эта оценка неточна, потому что мы не учли свойство двоичного дерева: на нижних уровнях узлов гораздо больше, чем на верхних**.
Далее выполним более точный расчет. Чтобы упростить вычисления, предположим, что дано "идеальное двоичное дерево" высоты $h$ с числом узлов $n$ ; это предположение не повлияет на корректность результата.
Далее выполним более точный расчет. Чтобы упростить вычисления, предположим, что дано «идеальное двоичное дерево» высоты $h$ с числом узлов $n$. Это предположение не повлияет на корректность результата.
![Число узлов на каждом уровне идеального двоичного дерева](build_heap.assets/heapify_operations_count.png){ class="animation-figure" }
<p align="center"> Рисунок 8-5 &nbsp; Число узлов на каждом уровне идеального двоичного дерева </p>
Как показано на рисунке 8-5, максимальное число итераций упорядочивания сверху вниз для некоторого узла равно расстоянию от этого узла до листового узла, а это расстояние как раз и есть высота узла. Поэтому мы можем просуммировать для каждого уровня выражение "число узлов $\times$ высота узла" и **получить суммарное число итераций упорядочивания для всех узлов**.
Как показано на рисунке 8-5, максимальное число итераций упорядочивания сверху вниз для некоторого узла равно расстоянию от этого узла до листового узла, а это расстояние как раз и есть высота узла. Поэтому мы можем просуммировать для каждого уровня выражение «число узлов $\times$ высота узла» и **получить суммарное число итераций упорядочивания для всех узлов**.
$$
T(h) = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{(h-1)}\times1
+7 -7
View File
@@ -43,7 +43,7 @@ comments: true
В реальных приложениях мы можем напрямую использовать классы кучи, предоставляемые языком программирования, или классы очереди с приоритетом.
Подобно сортировкам "по возрастанию" и "по убыванию", мы можем переключаться между "минимальной кучей" и "максимальной кучей", изменяя `flag` или модифицируя `Comparator` . Код приведен ниже:
Подобно сортировкам «по возрастанию» и «по убыванию», мы можем переключаться между «минимальной кучей» и «максимальной кучей», изменяя `flag` или модифицируя `Comparator` . Код приведен ниже:
=== "Python"
@@ -428,7 +428,7 @@ comments: true
### 1. &nbsp; Хранение и представление кучи
В разделе "Двоичные деревья" мы уже говорили, что полное двоичное дерево очень удобно представлять массивом. Поскольку куча как раз и является полным двоичным деревом, **для хранения кучи мы также будем использовать массив**.
В разделе «Двоичные деревья» мы уже говорили, что полное двоичное дерево очень удобно представлять массивом. Поскольку куча как раз и является полным двоичным деревом, **для хранения кучи мы также будем использовать массив**.
Когда двоичное дерево представляется массивом, элементы массива соответствуют значениям узлов, а индексы - положениям этих узлов в двоичном дереве. **Указатели на узлы реализуются через формулы отображения индексов**.
@@ -812,9 +812,9 @@ comments: true
### 3. &nbsp; Добавление элемента в кучу
Пусть дан элемент `val` . Сначала мы помещаем его в основание кучи. После добавления свойства кучи могут нарушиться, потому что `val` может оказаться больше, чем другие элементы в куче. **Поэтому необходимо восстановить порядок на пути от вставленного узла к корню** ; эта операция называется упорядочиванием кучи.
Пусть дан элемент `val` . Сначала мы помещаем его в основание кучи. После добавления свойства кучи могут нарушиться, потому что `val` может оказаться больше, чем другие элементы в куче. **Поэтому необходимо восстановить порядок на пути от вставленного узла к корню**. Эта операция называется упорядочиванием кучи.
Рассмотрим ситуацию, когда упорядочивание выполняется **снизу вверх**, начиная от только что вставленного узла. Как показано на рисунках ниже, мы сравниваем значение вставленного узла со значением его родителя; если вставленный узел больше, то меняем их местами. Затем продолжаем выполнять ту же операцию и последовательно восстанавливать корректность всех узлов по пути снизу вверх, пока не выйдем за корень или не встретим узел, для которого обмен не требуется.
Рассмотрим ситуацию, когда упорядочивание выполняется **снизу вверх**, начиная от только что вставленного узла. Как показано на рисунке 8-3, мы сравниваем значение вставленного узла со значением его родителя. Если вставленный узел больше, то меняем их местами. Затем продолжаем выполнять ту же операцию и последовательно восстанавливать корректность всех узлов по пути снизу вверх, пока не выйдем за корень или не встретим узел, для которого обмен не требуется.
=== "<1>"
![Шаги добавления элемента в кучу](heap.assets/heap_push_step1.png){ class="animation-figure" }
@@ -1223,7 +1223,7 @@ comments: true
2. После обмена удалить основание кучи из списка. Стоит отметить, что, поскольку обмен уже выполнен, фактически удаляется исходный элемент вершины кучи.
3. Начиная от корневого узла, **выполнить упорядочивание кучи сверху вниз**.
Как показано на рисунках ниже, **направление операции упорядочивания кучи сверху вниз противоположно операции упорядочивания кучи снизу вверх**. Мы сравниваем значение корневого узла со значениями двух дочерних узлов, выбираем больший дочерний узел и меняем его местами с корневым узлом. Затем циклически повторяем ту же операцию, пока не выйдем за листовой узел или не встретим узел, который уже не требует обмена.
Как показано на рисунке 8-4, **направление операции упорядочивания кучи сверху вниз противоположно операции упорядочивания кучи снизу вверх**. Мы сравниваем значение корневого узла со значениями двух дочерних узлов, выбираем больший дочерний узел и меняем его местами с корневым узлом. Затем циклически повторяем ту же операцию, пока не выйдем за листовой узел или не встретим узел, который уже не требует обмена.
=== "<1>"
![Шаги извлечения элемента с вершины кучи](heap.assets/heap_pop_step1.png){ class="animation-figure" }
@@ -1774,6 +1774,6 @@ comments: true
## 8.1.3 &nbsp; Типичные применения кучи
- **Очередь с приоритетом**: куча обычно является предпочтительной структурой данных для реализации очереди с приоритетом; добавление и извлечение элементов имеют временную сложность $O(\log n)$ , а построение кучи - $O(n)$ , и все эти операции выполняются очень эффективно.
- **Пирамидальная сортировка**: для заданного набора данных можно построить кучу, а затем непрерывно извлекать из нее элементы, получая отсортированные данные. Однако на практике мы обычно используем более изящную реализацию пирамидальной сортировки; подробности см. в разделе "Пирамидальная сортировка".
- **Очередь с приоритетом**: куча обычно является предпочтительной структурой данных для реализации очереди с приоритетом. Добавление и извлечение элементов имеют временную сложность $O(\log n)$ , а построение кучи - $O(n)$ , и все эти операции выполняются очень эффективно.
- **Пирамидальная сортировка**: для заданного набора данных можно построить кучу, а затем непрерывно извлекать из нее элементы, получая отсортированные данные. Однако на практике мы обычно используем более изящную реализацию пирамидальной сортировки. Подробности см. в разделе «Пирамидальная сортировка».
- **Получение наибольших $k$ элементов**: это классическая алгоритмическая задача и одновременно типичное применение кучи. Например, выбор 10 самых горячих новостей для списка популярных тем или выбор 10 самых продаваемых товаров.
+1 -1
View File
@@ -10,7 +10,7 @@ icon: material/family-tree
!!! abstract
Куча - это полное двоичное дерево, удовлетворяющее определенным условиям.
В максимальной и минимальной куче элемент на вершине всегда обладает самым выраженным приоритетом.
## Содержание главы
+2 -2
View File
@@ -7,7 +7,7 @@ comments: true
### 1. &nbsp; Ключевые выводы
- Куча представляет собой полное двоичное дерево и делится на максимальную кучу и минимальную кучу. Элемент на вершине максимальной (минимальной) кучи является наибольшим (наименьшим).
- Очередь с приоритетом определяется как очередь, элементы которой извлекаются в соответствии с приоритетом; обычно ее реализуют с помощью кучи.
- Очередь с приоритетом определяется как очередь, элементы которой извлекаются в соответствии с приоритетом. Обычно ее реализуют с помощью кучи.
- К основным операциям кучи и их временным сложностям относятся: добавление элемента в кучу $O(\log n)$ , извлечение элемента с вершины кучи $O(\log n)$ и доступ к вершине кучи $O(1)$ .
- Полное двоичное дерево очень удобно представлять массивом, поэтому кучу обычно тоже хранят в массиве.
- Операция упорядочивания кучи используется для поддержания свойств кучи и применяется как при добавлении элемента, так и при извлечении элемента.
@@ -16,6 +16,6 @@ comments: true
### 2. &nbsp; Q & A
**Q**: Является ли "куча" как структура данных тем же самым понятием, что и "куча" в управлении памятью?
**Q**: Является ли «куча» как структура данных тем же самым понятием, что и «куча» в управлении памятью?
Это не одно и то же, просто у них случайно совпало название. Куча в памяти компьютерной системы является частью динамического распределения памяти: во время выполнения программы она используется для хранения данных. Программа может запросить определенный объем памяти в куче для хранения сложных структур, таких как объекты и массивы. Когда эти данные больше не нужны, память нужно освободить, чтобы не допустить утечек. По сравнению со стековой памятью управление памятью в куче требует большей осторожности, а неправильное использование может привести к утечкам памяти и проблемам с указателями.
+5 -5
View File
@@ -12,7 +12,7 @@ comments: true
## 8.3.1 &nbsp; Метод 1: выбор через обход
Как показано на рисунке 8-6, можно выполнить $k$ проходов по массиву и на каждом проходе извлекать соответственно $1$-й, $2$-й, $\dots$ , $k$-й по величине элемент; временная сложность такого подхода равна $O(nk)$ .
Как показано на рисунке 8-6, можно выполнить $k$ проходов по массиву и на каждом проходе извлекать соответственно $1$-й, $2$-й, $\dots$ , $k$-й по величине элемент. Временная сложность такого подхода равна $O(nk)$ .
Этот метод подходит только для случая $k \ll n$ , потому что когда $k$ приближается к $n$ , его временная сложность стремится к $O(n^2)$ , а это уже очень затратно.
@@ -22,11 +22,11 @@ comments: true
!!! tip
Когда $k = n$ , мы получаем полную упорядоченную последовательность, и в этот момент задача становится эквивалентной алгоритму "сортировка выбором".
Когда $k = n$ , мы получаем полную упорядоченную последовательность, и в этот момент задача становится эквивалентной алгоритму «сортировка выбором».
## 8.3.2 &nbsp; Метод 2: сортировка
Как показано на рисунке 8-7, можно сначала отсортировать массив `nums` , а затем вернуть его крайние правые $k$ элементов; временная сложность такого метода равна $O(n \log n)$ .
Как показано на рисунке 8-7, можно сначала отсортировать массив `nums` , а затем вернуть его крайние правые $k$ элементов. Временная сложность такого метода равна $O(n \log n)$ .
Очевидно, что этот способ делает слишком много, потому что нам нужно только найти наибольшие $k$ элементов, а сортировать остальные элементы совсем не обязательно.
@@ -36,7 +36,7 @@ comments: true
## 8.3.3 &nbsp; Метод 3: куча
Задачу Top-k можно решить гораздо эффективнее с помощью кучи, как показано на рисунках ниже.
Задачу Top-k можно решить гораздо эффективнее с помощью кучи, как показано на рисунке 8-8.
1. Инициализировать минимальную кучу, у которой вершина содержит наименьший элемент.
2. Сначала по очереди поместить в кучу первые $k$ элементов массива.
@@ -466,6 +466,6 @@ comments: true
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=import%20heapq%0A%0Adef%20top_k_heap%28nums%3A%20list%5Bint%5D%2C%20k%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22%D0%9D%D0%B0%D0%B9%D1%82%D0%B8%20k%20%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B8%D1%85%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20%D0%BA%D1%83%D1%87%D0%B8%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%20%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9%20%D0%BA%D1%83%D1%87%D0%B8%0A%20%20%20%20heap%20%3D%20%5B%5D%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B5%20k%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%20%D0%B2%20%D0%BA%D1%83%D1%87%D1%83%0A%20%20%20%20for%20i%20in%20range%28k%29%3A%0A%20%20%20%20%20%20%20%20heapq.heappush%28heap%2C%20nums%5Bi%5D%29%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B8%D0%BD%D0%B0%D1%8F%20%D1%81%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%20k%2B1%2C%20%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B4%D0%BB%D0%B8%D0%BD%D1%83%20%D0%BA%D1%83%D1%87%D0%B8%20%D1%80%D0%B0%D0%B2%D0%BD%D0%BE%D0%B9%20k%0A%20%20%20%20for%20i%20in%20range%28k%2C%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%20%D0%BD%D0%B0%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%B5%20%D0%BA%D1%83%D1%87%D0%B8%2C%20%D0%B8%D0%B7%D0%B2%D0%BB%D0%B5%D1%87%D1%8C%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%83%20%D0%BA%D1%83%D1%87%D0%B8%20%D0%B8%20%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%D0%B2%20%D0%BA%D1%83%D1%87%D1%83%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3E%20heap%5B0%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappop%28heap%29%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappush%28heap%2C%20nums%5Bi%5D%29%0A%20%20%20%20return%20heap%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1%2C%207%2C%206%2C%203%2C%202%5D%0A%20%20%20%20k%20%3D%203%0A%0A%20%20%20%20res%20%3D%20top_k_heap%28nums%2C%20k%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=6&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=import%20heapq%0A%0Adef%20top_k_heap%28nums%3A%20list%5Bint%5D%2C%20k%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22%D0%9D%D0%B0%D0%B9%D1%82%D0%B8%20k%20%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B8%D1%85%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20%D0%BA%D1%83%D1%87%D0%B8%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%20%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9%20%D0%BA%D1%83%D1%87%D0%B8%0A%20%20%20%20heap%20%3D%20%5B%5D%0A%20%20%20%20%23%20%D0%9F%D0%BE%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B5%20k%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%20%D0%B2%20%D0%BA%D1%83%D1%87%D1%83%0A%20%20%20%20for%20i%20in%20range%28k%29%3A%0A%20%20%20%20%20%20%20%20heapq.heappush%28heap%2C%20nums%5Bi%5D%29%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B8%D0%BD%D0%B0%D1%8F%20%D1%81%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%20k%2B1%2C%20%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B4%D0%BB%D0%B8%D0%BD%D1%83%20%D0%BA%D1%83%D1%87%D0%B8%20%D1%80%D0%B0%D0%B2%D0%BD%D0%BE%D0%B9%20k%0A%20%20%20%20for%20i%20in%20range%28k%2C%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%20%D0%BD%D0%B0%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%B5%20%D0%BA%D1%83%D1%87%D0%B8%2C%20%D0%B8%D0%B7%D0%B2%D0%BB%D0%B5%D1%87%D1%8C%20%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%83%20%D0%BA%D1%83%D1%87%D0%B8%20%D0%B8%20%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%D0%B2%20%D0%BA%D1%83%D1%87%D1%83%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3E%20heap%5B0%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappop%28heap%29%0A%20%20%20%20%20%20%20%20%20%20%20%20heapq.heappush%28heap%2C%20nums%5Bi%5D%29%0A%20%20%20%20return%20heap%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1%2C%207%2C%206%2C%203%2C%202%5D%0A%20%20%20%20k%20%3D%203%0A%0A%20%20%20%20res%20%3D%20top_k_heap%28nums%2C%20k%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=6&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
Всего выполняется $n$ операций добавления и извлечения из кучи, а максимальная длина кучи равна $k$ , поэтому временная сложность равна $O(n \log k)$ . Этот метод очень эффективен: когда $k$ мало, временная сложность стремится к $O(n)$ ; когда $k$ велико, она все равно не превышает $O(n \log n)$ .
Всего выполняется $n$ операций добавления и извлечения из кучи, а максимальная длина кучи равна $k$ , поэтому временная сложность равна $O(n \log k)$ . Этот метод очень эффективен: когда $k$ мало, временная сложность стремится к $O(n)$. Когда $k$ велико, она все равно не превышает $O(n \log n)$ .
Кроме того, этот метод подходит и для сценариев с динамическим потоком данных. При непрерывном поступлении новых данных мы можем продолжать поддерживать содержимое кучи, тем самым динамически обновляя наибольшие $k$ элементов.