Биномиальная куча — различия между версиями
(→Представление биномиальных куч) |
|||
Строка 49: | Строка 49: | ||
{{Утверждение | {{Утверждение | ||
− | |statement=В биномиальном дереве <tex>B_k</tex> с <tex>n</tex> вершинами максимальная степень произвольного узла равна <tex>\log | + | |statement=В биномиальном дереве <tex>B_k</tex> с <tex>n</tex> вершинами максимальная степень произвольного узла равна <tex>\log n</tex>. |
|proof= | |proof= | ||
− | Докажем это утверждение для корня. Степень остальных вершин меньше по предыдущему свойству. Так как степень корня дерева порядка <tex>k</tex> равна <tex>k</tex>, а узлов в этом дереве <tex>n = 2^k</tex>, то прологарифмировав обе части получаем, что <tex>k=O(\log | + | Докажем это утверждение для корня. Степень остальных вершин меньше по предыдущему свойству. Так как степень корня дерева порядка <tex>k</tex> равна <tex>k</tex>, а узлов в этом дереве <tex>n = 2^k</tex>, то прологарифмировав обе части получаем, что <tex>k=O(\log n)</tex>, то степень произвольного узла не более <tex>\log n</tex>. |
}} | }} | ||
Строка 76: | Строка 76: | ||
Рассмотрим операции, которые можно производить с биномиальной кучей. Время работы указано в таблице: | Рассмотрим операции, которые можно производить с биномиальной кучей. Время работы указано в таблице: | ||
{| border="1" | {| border="1" | ||
− | |insert | + | |<math>\mathrm {insert}</math> |
− | |<tex>O(\log | + | |<tex>O(\log n)</tex> |
|- | |- | ||
− | |getMinimum | + | |<math>\mathrm {getMinimum}</math> |
− | |<tex>O(\log | + | |<tex>O(\log n)</tex> |
|- | |- | ||
− | |extractMin | + | |<math>\mathrm {extractMin}</math> |
− | |<tex>\Theta(\log | + | |<tex>\Theta(\log n)</tex> |
|- | |- | ||
− | |merge | + | |<math>\mathrm {merge}</math> |
− | |<tex>\Omega(\log | + | |<tex>\Omega(\log n)</tex> |
|- | |- | ||
− | |decreaseKey | + | |<math>\mathrm {decreaseKey}</math> |
− | |<tex>\Theta(\log | + | |<tex>\Theta(\log n)</tex> |
|- | |- | ||
− | |delete | + | |<math>\mathrm {delete}</math> |
− | |<tex>\Theta(\log | + | |<tex>\Theta(\log n)</tex> |
|} | |} | ||
Обозначим нашу кучу за <tex>H</tex>. То пусть <tex>H.head</tex> {{---}} указатель на корень биномиального дерева минимального порядка этой кучи. Изначально <tex>H.head = null</tex>, то есть куча не содержит элементов. | Обозначим нашу кучу за <tex>H</tex>. То пусть <tex>H.head</tex> {{---}} указатель на корень биномиального дерева минимального порядка этой кучи. Изначально <tex>H.head = null</tex>, то есть куча не содержит элементов. | ||
Строка 99: | Строка 99: | ||
Для нахождения минимального элемента надо найти элемент в списке корней с минимальным значением (предполагается, что ключей, равных <tex>\infty</tex>, нет). | Для нахождения минимального элемента надо найти элемент в списке корней с минимальным значением (предполагается, что ключей, равных <tex>\infty</tex>, нет). | ||
− | Так как корней в этом списке не более <tex>\lfloor \log | + | Так как корней в этом списке не более <tex>\lfloor \log n \rfloor + 1</tex>, то операция выполняется за <tex>O(\log n)</tex>. |
При вызове этой процедуры для кучи, изображенной на картинке ниже, будет возвращен указатель на вершину с ключом <tex>1</tex>. | При вызове этой процедуры для кучи, изображенной на картинке ниже, будет возвращен указатель на вершину с ключом <tex>1</tex>. | ||
Строка 114: | Строка 114: | ||
Работа этой процедуры начинается с соединения корневых списков куч в единый список, в котором корневые вершины идут в порядке неубывания их степеней. | Работа этой процедуры начинается с соединения корневых списков куч в единый список, в котором корневые вершины идут в порядке неубывания их степеней. | ||
− | В получившемся списке могут встречаться пары соседних вершин одинаковой степени. Поэтому мы начинаем соединять деревья равной степени и делаем это до тех пор, пока деревьев одинаковой степени не останется. Этот процесс соответствует сложению двоичных чисел столбиком, и время его работы пропорционально числу корневых вершин, то есть операция выполняется за <tex>\Omega(\log | + | В получившемся списке могут встречаться пары соседних вершин одинаковой степени. Поэтому мы начинаем соединять деревья равной степени и делаем это до тех пор, пока деревьев одинаковой степени не останется. Этот процесс соответствует сложению двоичных чисел столбиком, и время его работы пропорционально числу корневых вершин, то есть операция выполняется за <tex>\Omega(\log n)</tex>. |
Строка 168: | Строка 168: | ||
=== insert === | === insert === | ||
− | Чтобы добавить новый элемент в биномиальную кучу нужно создать биномиальную кучу <tex>H'</tex> с единственным узлом, содержащим этот элемент, за время <tex>O(1)</tex> и объединить ее с биномиальной кучей <tex>H</tex> за <tex>O(\log | + | Чтобы добавить новый элемент в биномиальную кучу нужно создать биномиальную кучу <tex>H'</tex> с единственным узлом, содержащим этот элемент, за время <tex>O(1)</tex> и объединить ее с биномиальной кучей <tex>H</tex> за <tex>O(\log n)</tex>, так как в данном случае куча <tex>H'</tex> содержит лишь одно дерево. |
=== extractMin === | === extractMin === | ||
Строка 177: | Строка 177: | ||
* Найдем биномиальное дерево с минимальным корневым значением. Предположим, что это дерево <tex>B_k</tex>. Время работы этого шага алгоритма <tex>\Theta(\log n)</tex>. | * Найдем биномиальное дерево с минимальным корневым значением. Предположим, что это дерево <tex>B_k</tex>. Время работы этого шага алгоритма <tex>\Theta(\log n)</tex>. | ||
* Удаляем дерево <tex>B_k</tex> из кучи <tex>H</tex>. Иными словами удаляем его корень из списка корней кучи. Это можно сделать за время <tex>O(1)</tex>. | * Удаляем дерево <tex>B_k</tex> из кучи <tex>H</tex>. Иными словами удаляем его корень из списка корней кучи. Это можно сделать за время <tex>O(1)</tex>. | ||
− | * Пусть <tex>H'</tex> {{---}} куча детей найденного корня. При этом мы для каждого из ребенка устанавливаем указатель на предка равным <tex>null</tex>. После этого сливаем кучу <tex>H'</tex> c <tex>H</tex> за <tex>\Omega(\log | + | * Пусть <tex>H'</tex> {{---}} куча детей найденного корня. При этом мы для каждого из ребенка устанавливаем указатель на предка равным <tex>null</tex>. После этого сливаем кучу <tex>H'</tex> c <tex>H</tex> за <tex>\Omega(\log n)</tex>. |
− | Процедура выполняется за время <tex>\Theta(\log | + | Процедура выполняется за время <tex>\Theta(\log n)</tex>, поскольку всего в списке <tex>\Theta(\log n)</tex> корней биномиальных деревьев. И всего у найденного дерева <tex> k </tex> порядка (с минимальным значением ключа) ровно <tex> k </tex> детей, то сложность перебора этих детей будет тоже <tex>\Theta(\log n)</tex>. А процесс слияния выполняется за <tex>\Omega(\log n)</tex>. Таким образом операция выполняется <tex>\Theta(\log n)</tex>. |
[[Файл:BinHeapExampleNew31.png|700px|Примеp извлечения минимума]] | [[Файл:BinHeapExampleNew31.png|700px|Примеp извлечения минимума]] | ||
Строка 225: | Строка 225: | ||
=== decreaseKey === | === decreaseKey === | ||
− | Следующая процедура уменьшает ключ элемента <tex>x</tex> биномиальной кучи, присваивая ему новое значение. Вершина, ключ которой был уменьшен, «всплывает» как в обычной куче. Процедура выполняется за время <tex>\Theta(\log | + | Следующая процедура уменьшает ключ элемента <tex>x</tex> биномиальной кучи, присваивая ему новое значение. Вершина, ключ которой был уменьшен, «всплывает» как в обычной куче. Процедура выполняется за время <tex>\Theta(\log n)</tex>, поскольку глубина вершины <tex>x</tex> в худшем случае есть <tex>\Theta(\log n)</tex> (свойства биномиального дерева), а при выполнении каждого шага алгоритма мы поднимаемся вверх. |
<code> | <code> | ||
Строка 249: | Строка 249: | ||
=== delete === | === delete === | ||
− | Удаление ключа сводится к операциям decreaseKey и extractMin: сначала нужно уменьшить ключ до минимально возможного значения, а затем извлечь вершину с минимальным ключом. В процессе выполнения процедуры этот узел всплывает вверх, откуда и удаляется. Процедура выполняется за время <tex>\Theta(\log | + | Удаление ключа сводится к операциям <math>\mathrm {decreaseKey}</math> и <math>\mathrm {extractMin}</math>: сначала нужно уменьшить ключ до минимально возможного значения, а затем извлечь вершину с минимальным ключом. В процессе выполнения процедуры этот узел всплывает вверх, откуда и удаляется. Процедура выполняется за время <tex>\Theta(\log n)</tex>, поскольку каждая из операций, которые используется в реализации, работают за <tex>\Theta(\log n)</tex>. |
<code> | <code> | ||
Строка 259: | Строка 259: | ||
</code> | </code> | ||
− | == Источники == | + | == Источники информации == |
* [http://www.intuit.ru/department/algorithms/dscm/7/ Биномиальные кучи — INTUIT.ru] | * [http://www.intuit.ru/department/algorithms/dscm/7/ Биномиальные кучи — INTUIT.ru] | ||
* [http://en.wikipedia.org/wiki/Binomial_heap Binomial heap — Wikipedia] | * [http://en.wikipedia.org/wiki/Binomial_heap Binomial heap — Wikipedia] |
Версия 22:12, 15 июня 2014
Содержание
Биномиальное дерево
Определение: |
Биномиальное дерево дерево, определяемое для каждого следующим образом: — дерево, состоящее из одного узла; состоит из двух биномиальных деревьев , связанны вместе таким образом, что корень одного из них является дочерним узлом корня второго дерева. | —
Свойства биномиальных деревьев
Утверждение: |
Биномиальное дерево с вершинами имеет узлов |
Докажем по индукции: База Так как в дереве порядка — верно. Пусть для некоторого условие верно, то докажем, что для это также верно: вдвое больше узлов, чем в дереве порядка , то дерево порядка имеет узлов. Переход доказан, то биномиальное дерево с вершинами имеет узлов. |
Утверждение: |
Биномиальное дерево с вершинами имеет высоту ; |
Докажем по индукции: База Так как в дереве порядка — верно. Пусть для некоторого условие верно, то докажем, что для это также верно: высота больше на (так как мы подвешиваем к текущему дереву дерево того же порядка), чем в дереве порядка , то дерево порядка имеет высоту . Переход доказан, то биномиальное дерево с вершинами имеет высоту . |
Утверждение: |
Биномиальное дерево с вершинами имеет ровно узлов на высоте ; |
Докажем по индукции: База Рассмотрим — верно. Пусть для некоторого условие верно, то докажем, что для это также верно: уровень дерева . Дерево было получено подвешиванием одного дерева порядка к другому. Тогда на уровне дерева всего узлов , так как от подвешенного дерева в дерево порядка нам пришли узлы глубины . То для -го уровня дерева количество узлов . Переход доказан, то биномиальное дерево с вершинами имеет ровно узлов на высоте . |
Утверждение: |
Биномиальное дерево с вершинами имеет корень степени ; степень всех остальных вершин меньше степени корня биномиального дерева; |
Так как в дереве порядка | степень корня больше на , чем в дереве порядка , а в дереве нулевого порядка степень корня , то дерево порядка имеет корень степени . И так как при таком увеличении порядка (при переходе от дерева порядка к ) в полученном дереве лишь степень корня возрастает, то доказываемый инвариант, то есть степень корня больше степени остальных вершин, не будет нарушаться.
Утверждение: |
В биномиальном дереве с вершинами максимальная степень произвольного узла равна . |
Докажем это утверждение для корня. Степень остальных вершин меньше по предыдущему свойству. Так как степень корня дерева порядка | равна , а узлов в этом дереве , то прологарифмировав обе части получаем, что , то степень произвольного узла не более .
Биномиальная куча
Определение: |
Биномиальная куча представляет собой множество биномиальных деревьев, которые удовлетворяют следующим свойствам:
|
Представление биномиальных куч
Поскольку количество детей у узлов варьируется в широких пределах, ссылка на детей осуществляется через левого ребенка, а остальные дети образуют односвязный список. Каждый узел в биномиальной куче представляется набором полей:
- — ключ (вес) элемента;
- — указатель на родителя узла;
- — указатель на левого ребенка узла;
- — указатель на правого брата узла;
- — степень узла (количество дочерних узлов данного узла).
Корни деревьев, из которых состоит куча, содержатся в так называемом списке корней, при проходе по которому степени соответствующих корней находятся в возрастающем порядке. Доступ к куче осуществляется ссылкой на первый корень в списке корней.
Операции над биномиальными кучами
Рассмотрим операции, которые можно производить с биномиальной кучей. Время работы указано в таблице:
Обозначим нашу кучу за
. То пусть — указатель на корень биномиального дерева минимального порядка этой кучи. Изначально , то есть куча не содержит элементов.getMinimum
Для нахождения минимального элемента надо найти элемент в списке корней с минимальным значением (предполагается, что ключей, равных
, нет).Так как корней в этом списке не более
, то операция выполняется за .При вызове этой процедуры для кучи, изображенной на картинке ниже, будет возвращен указатель на вершину с ключом
.merge
Эта операция, соединяющая две биномиальные кучи в одну, используется в качестве подпрограммы большинством остальных операций.
Вот в чем состоит ее суть: пусть есть две биномиальные кучи с
и . Размеры деревьев в кучах соответствуют двоичным числам и , то есть при наличии дерева соответствующего порядка в этом разряде числа стоит единица, иначе ноль. При сложении столбиком в двоичной системе происходят переносы, которые соответствуют слияниям двух биномиальных деревьев в дерево . Надо только посмотреть, в каком из сливаемых деревьев корень меньше, и считать его верхним (пример работы для одного случая приведен на рисунке справа; в другом случае подвешиваем наоборот).Работа этой процедуры начинается с соединения корневых списков куч в единый список, в котором корневые вершины идут в порядке неубывания их степеней.
В получившемся списке могут встречаться пары соседних вершин одинаковой степени. Поэтому мы начинаем соединять деревья равной степени и делаем это до тех пор, пока деревьев одинаковой степени не останется. Этот процесс соответствует сложению двоичных чисел столбиком, и время его работы пропорционально числу корневых вершин, то есть операция выполняется за
.
Node merge(H1, H2) if H1 == null return H2 if H2 == null return H1 // H - результат слияния H.head = null // Слияние корневых списков curH = H.head curH1 = H1.head curH2 = H2.head while curH1 != null && curH2 != null if curH1.degree < curH2.degree curH.sibling = curH1 curH = curH1 curH1 = curH1.sibling else curH.sibling = curH2 curH = curH2 curH2 = curH2.sibling if curH1 == null while curH2 != null curH.sibling = curH2 curH2 = curH2.sibling else while curH1 != null curH.sibling = curH1 curH1 = curH1.sibling // объединение деревьев одной степени curH = H.head while curH.sibling != null if curH.degree == curH.sibling.degree p[curH] = curH.sibling tmp = curH.sibling curH.sibling = curH.sibling.child curH = tmp continue curH = curH.sibling return H
insert
Чтобы добавить новый элемент в биномиальную кучу нужно создать биномиальную кучу
с единственным узлом, содержащим этот элемент, за время и объединить ее с биномиальной кучей за , так как в данном случае куча содержит лишь одно дерево.extractMin
Приведенная ниже процедура извлекает узел с минимальным ключом из биномиальной кучи и возвращает указатель на извлеченный узел.
Рассмотрим пошагово алгоритм:
- Найдем биномиальное дерево с минимальным корневым значением. Предположим, что это дерево . Время работы этого шага алгоритма .
- Удаляем дерево из кучи . Иными словами удаляем его корень из списка корней кучи. Это можно сделать за время .
- Пусть — куча детей найденного корня. При этом мы для каждого из ребенка устанавливаем указатель на предка равным . После этого сливаем кучу c за .
Процедура выполняется за время
, поскольку всего в списке корней биномиальных деревьев. И всего у найденного дерева порядка (с минимальным значением ключа) ровно детей, то сложность перебора этих детей будет тоже . А процесс слияния выполняется за . Таким образом операция выполняется .
Node extractMin(H)
//поиск корня х с минимальным значением ключа в списке корней Н:
min =
x = null
xBefore = null
curx = H.head
curxBefore = null
while curx != null
// релаксируем текущий минимум
if curx.key < min
min = curx.key
x = curx
xBefore = curxBefore
curxBefore = curx
curx = curx.sibling
//удаление найденного корня x из списка корней деревьев кучи
if (xBefore == null)
H.head = x.sibling
else
xBefore.sibling = x.sibling
//построение кучи детей вершины x, при этом изменяем предка соответствующего ребенка на null:
H' = null
curx = x.child
H'.head = x.child
while curx != null
// меняем указатель на родителя узла curx
p[curx] = null
// переход к следующему ребенку
curx = curx.sibling
// слияние нашего дерева с деревом H'
H = merge(H, H')
return x
decreaseKey
Следующая процедура уменьшает ключ элемента
биномиальной кучи, присваивая ему новое значение. Вершина, ключ которой был уменьшен, «всплывает» как в обычной куче. Процедура выполняется за время , поскольку глубина вершины в худшем случае есть (свойства биномиального дерева), а при выполнении каждого шага алгоритма мы поднимаемся вверх.
void decreaseKey(H, x, k) // проверка на то, что текущий ключ не меньше передаваемого ключа k if k > key[x] return key[x] = k y = x z = p[y] //поднимаем текущий элемент x с новым ключом k, пока //это значение меньше значения в родительской вершине while z != null and key[y] < key[z] swap(key[y], key[z]) y = z z = p[y]
Пример работы процедуры проиллюстрирован на рисунке (
— уменьшаемый элемент, — его предок).delete
Удаление ключа сводится к операциям
и : сначала нужно уменьшить ключ до минимально возможного значения, а затем извлечь вершину с минимальным ключом. В процессе выполнения процедуры этот узел всплывает вверх, откуда и удаляется. Процедура выполняется за время , поскольку каждая из операций, которые используется в реализации, работают за .
void delete(H, x)
//уменьшение ключа до минимально возможного значения
decreaseKey(H, x,
)
//удаление "всплывшего" элемента
extractMin(H)
Источники информации
- Биномиальные кучи — INTUIT.ru
- Binomial heap — Wikipedia
- Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 538—558. — ISBN 5-8489-0857-4