Тонкая куча — различия между версиями
Genyaz (обсуждение | вклад) |
Genyaz (обсуждение | вклад) (Амортизационный анализ операций) |
||
| Строка 14: | Строка 14: | ||
== Свойства тонкого дерева == | == Свойства тонкого дерева == | ||
{{Утверждение | {{Утверждение | ||
| − | |id= | + | |id=about_thin_tree. |
| − | |statement=Для любого узла <tex>x</tex> либо <tex>Degree(x)=Rank(x)</tex>, в этом случае говорим, что узел <tex>x</tex> не помечен (полный); либо <tex>Degree(x)=Rank(x)-1</tex>, в этом случае говорим, что узел <tex>x</tex> помечен (неполный). | + | |statement=Тонкое дерево обладает следующими свойтсвами: |
| − | + | # Для любого узла <tex>x</tex> либо <tex>Degree(x)=Rank(x)</tex>, в этом случае говорим, что узел <tex>x</tex> не помечен (полный); либо <tex>Degree(x)=Rank(x)-1</tex>, в этом случае говорим, что узел <tex>x</tex> помечен (неполный). | |
| − | + | # Корень не помечен (полный). | |
| − | + | # Для любого узла <tex>x</tex> ранги его детей от самого правого к самому левому равны соответственно <tex>0,1,2,...,Degree(x)-1</tex>. | |
| − | + | # Узел <tex>x</tex> помечен тогда и только тогда, если его ранг на 2 больше, чем ранг его самого левого сына, или его ранг равен 1 и он не имеет детей. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
}} | }} | ||
| Строка 86: | Строка 78: | ||
== Операции над тонкой кучей == | == Операции над тонкой кучей == | ||
| − | Рассмотрим операции, которые можно производить | + | Рассмотрим операции, которые можно производить над тонкой кучей. Время работы указано в таблице: |
{| border="1" | {| border="1" | ||
|-align="center" | |-align="center" | ||
| Строка 113: | Строка 105: | ||
Многие операции над тонкой кучей выполняются так же, как и над фиббоначиевой. | Многие операции над тонкой кучей выполняются так же, как и над фиббоначиевой. | ||
| + | Для амортизационного анализа применим метод потенциалов. | ||
| + | |||
| + | Пусть функция потенциала определена как <tex>\Phi = n + 2 \cdot m</tex> где <tex>n</tex> {{---}} это количество ''тонких деревьев'' в куче, а <tex>m</tex> {{---}} это количество помеченных вершин. | ||
| + | |||
| + | {{Утверждение | ||
| + | |id=about_thin_heap_potential. | ||
| + | |statement=Определённый таким образом потенциал обладает свойствами: | ||
| + | # <tex>\Phi \geqslant 0</tex>. | ||
| + | # Для пустой кучи <tex>\Phi = 0</tex>. | ||
| + | }} | ||
| + | |||
| + | |||
=== makeHeap === | === makeHeap === | ||
| − | Возвращаем новый пустой корневой список. | + | Возвращаем новый пустой корневой список, его потенциал <tex>\Phi=0</tex>. |
| + | |||
| + | Стоимость <tex>O(1)</tex>. | ||
=== insert === | === insert === | ||
| − | Создаем новое тонкое дерево из единственного узла с ключом <tex>x</tex>, добавляем в корневой список на первое место, если ключ минимален, иначе на второе. | + | Создаем новое тонкое дерево из единственного узла с ключом <tex>x</tex>, добавляем в корневой список на первое место, если ключ минимален, иначе на второе. Потенциал <tex>\Phi</tex> увеличивается на 1. |
| + | |||
| + | Стоимость <tex>O(1)</tex>. | ||
=== getMin === | === getMin === | ||
| − | Обращаемся к первому корневому узлу списка. | + | Обращаемся к первому корневому узлу списка, потенциал <tex>\Phi</tex> не меняется. |
| + | |||
| + | Стоимость <tex>O(1)</tex>. | ||
=== meld === | === meld === | ||
| − | Сливаем корневые списки, ставя первым тот список, где ключ первого корня минимален. | + | Сливаем корневые списки, ставя первым тот список, где ключ первого корня минимален. Суммарный потенциал <tex>\Phi</tex> не меняется. |
| + | |||
| + | Стоимость <tex>O(1)</tex>. | ||
=== extractMin === | === extractMin === | ||
| − | Удаляем корень с минимальным ключом из корневого списка | + | # Удаляем корень с минимальным ключом из корневого списка. |
| − | + | # Для всех его помеченных детей уменьшаем ранг и делаем их нормальными. | |
| + | # Cливаем детей с корневым списком. | ||
| + | # Объединяем, пока возможно, тонкие деревья одного ранга. | ||
| + | Это можно сделать, например, с помощью вспомогательного массива размером <tex>O(D(n))</tex>, в <tex>i</tex>-ой ячейке которго хранится корень тонкого дерева <tex>T_i</tex> ранга <tex>i</tex>. | ||
| + | |||
| + | Изначально массив пуст, а мы добавляем в него все деревья нашего корневого списка. | ||
| + | |||
| + | При добавлении нового дерева мы, пока возможно, связываем его с деревом такого же ранга, а затем пытаемся добавить новое дерево с рангом на <tex>1</tex> больше. | ||
| + | |||
| + | Пусть мы сделали <tex>ls</tex> ''связывающих шагов'' (''linking steps'') во время добавления в массив. | ||
| + | |||
| + | Мы удалили корень из списка за <tex>O(1)</tex>, затем за <tex>O(D(n))</tex> нормализовали детей корня и добавили в корневой список, а затем за <tex>O(D(n))+O(ls)</tex> получили новый корневой список, в котором за <tex>O(D(n))</tex> нашли минимальный корень и подвесили список за него. | ||
| + | |||
| + | Получили фактическую стоимость <tex>O(D(n))+O(ls)</tex>. С другой стороны, при добавлении детей в список мы увеличили потенциал <tex>\Phi</tex> не более чем на <tex>O(D(n))</tex>, а каждый связывающий шаг уменьшает наш потенциал <tex>\Phi</tex> на <tex>1</tex>. | ||
| + | |||
| + | Cтоимость <tex>O(D(n))=O(\log(n))</tex>. | ||
=== decreaseKey === | === decreaseKey === | ||
| − | + | Стоимость <tex>O(1)</tex>. | |
=== delete === | === delete === | ||
| − | Сначала выполняем <tex>decreaseKey</tex> этого элемента до <tex>-\infty</tex>, затем выполняем <tex>extractMin</tex>. | + | Сначала выполняем <tex>decreaseKey</tex> этого элемента до <tex>-\infty</tex>, затем выполняем <tex>extractMin</tex>. |
| + | |||
| + | Стоимость <tex>O(\log(n))</tex>. | ||
Версия 10:21, 24 мая 2013
Тонкая куча — это структура данных, реализующая приоритетную очередь с теми же асимптотическими оценками, что и фиббоначиева куча, но имеющая большую практическую ценность из-за меньших констант.
Тонкие кучи, как и многие другие кучеобразные структуры, аналогичны биномиальным кучам.
Содержание
Тонкое дерево
| Определение: |
| Тонкое дерево (thin tree) ранга — это дерево, которое может быть получено из биномиального дерева удалением у нескольких внутренних, то есть не являющихся корнем или листом, узлов самого левого сына. |
Заметим, что у листьев детей нет, а если у корня удалить самого левого сына, то превратится в . Ранг тонкого дерева равен количеству детей корня.
Для любого узла в дереве обозначим: — количество детей узла ; — ранг соответствующего узла в биномиальном дереве .
Свойства тонкого дерева
| Утверждение: |
Тонкое дерево обладает следующими свойтсвами:
|
Тонкая куча
| Определение: |
| Тонкий лес (thin forest) — это набор тонких деревьев, ранги которых не обязательно попарно различны. |
| Утверждение: |
Для любого натурального числа существует тонкий лес, который содержит ровно элементов и состоит из тонких деревьев попарно различных рангов. |
| Действительно, любой биномиальный лес является тонким, а для биномиального леса рассматриваемое утверждение справедливо. |
| Определение: |
| Тонкая куча (thin heap) — это кучеобразно нагруженный тонкий лес. |
Пусть — максимально возможный ранг узла в тонкой куче, содержащей элементов.
| Теорема (О максимальном ранге узла): |
В тонкой куче из элементов , где — золотое сечение. |
| Доказательство: |
|
Сначала покажем, что узел ранга в тонком дереве имеет не менее потомков, включая самого себя, где — -е число Фибоначчи, определяемое соотношениями , , для . Действительно, пусть — минимально возможное число узлов, включая самого себя, в тонком дереве ранга . По свойствам и тонкого дерева получаем следующие соотношения: для Числа Фибоначчи удовлетворяют этому же рекуррентному соотношению, причем неравенство можно заменить равенством. Отсюда по индукции следует, что для любых . Неравенство хорошо известно. Теперь убедимся в том, что максимально возможный ранг тонкого дерева в тонкой куче, содержащей элементов, не превосходит числа . Действительно, выберем в тонкой куче дерево максимального ранга. Пусть — количество вершин в этом дереве, тогда . Отсюда следует, что . |
Представление тонкой кучи
Поскольку при работе с тонкой кучей ссылка на родителя требуется только у самого левого ее ребенка, можно хранить ее вместо ссылки на левого сына этой вершины.
Таким образом, для эффективной работы тонкой кучи необходимы следующие поля узла:
- — ключ (вес) элемента;
- — указатель на самого левого ребенка узла;
- — указатель на правого брата узла, либо на следующий корень, если текущий узел корень;
- — указатель на левого брата узла, либо на родителя, если текущий узел самый левый, либо null, если это первый корень списка;
- — ранг узла (количество дочерних узлов данного узла).
Отдельно должен храниться односвязный список корней, корень с минимальным ключом должне быть первым в этом списке.
Для ускорения проверки на тонкость (thinness) можно отдельно хранить помеченность вершины.
Также в вершине можно хранить любую дополнительную информацию.
Операции над тонкой кучей
Рассмотрим операции, которые можно производить над тонкой кучей. Время работы указано в таблице:
Многие операции над тонкой кучей выполняются так же, как и над фиббоначиевой.
Для амортизационного анализа применим метод потенциалов.
Пусть функция потенциала определена как где — это количество тонких деревьев в куче, а — это количество помеченных вершин.
| Утверждение: |
Определённый таким образом потенциал обладает свойствами:
|
makeHeap
Возвращаем новый пустой корневой список, его потенциал .
Стоимость .
insert
Создаем новое тонкое дерево из единственного узла с ключом , добавляем в корневой список на первое место, если ключ минимален, иначе на второе. Потенциал увеличивается на 1.
Стоимость .
getMin
Обращаемся к первому корневому узлу списка, потенциал не меняется.
Стоимость .
meld
Сливаем корневые списки, ставя первым тот список, где ключ первого корня минимален. Суммарный потенциал не меняется.
Стоимость .
extractMin
- Удаляем корень с минимальным ключом из корневого списка.
- Для всех его помеченных детей уменьшаем ранг и делаем их нормальными.
- Cливаем детей с корневым списком.
- Объединяем, пока возможно, тонкие деревья одного ранга.
Это можно сделать, например, с помощью вспомогательного массива размером , в -ой ячейке которго хранится корень тонкого дерева ранга .
Изначально массив пуст, а мы добавляем в него все деревья нашего корневого списка.
При добавлении нового дерева мы, пока возможно, связываем его с деревом такого же ранга, а затем пытаемся добавить новое дерево с рангом на больше.
Пусть мы сделали связывающих шагов (linking steps) во время добавления в массив.
Мы удалили корень из списка за , затем за нормализовали детей корня и добавили в корневой список, а затем за получили новый корневой список, в котором за нашли минимальный корень и подвесили список за него.
Получили фактическую стоимость . С другой стороны, при добавлении детей в список мы увеличили потенциал не более чем на , а каждый связывающий шаг уменьшает наш потенциал на .
Cтоимость .
decreaseKey
Стоимость .
delete
Сначала выполняем этого элемента до , затем выполняем .
Стоимость .