Тонкая куча — различия между версиями
Genyaz (обсуждение | вклад) (Амортизационный анализ операций) |
Genyaz (обсуждение | вклад) м |
||
Строка 8: | Строка 8: | ||
}} | }} | ||
− | + | Ограничение на принадлежность внутренним узлам вызвано тем, что у листьев детей нет, а если у корня <tex>B_k</tex> удалить самого левого сына, то <tex>B_k</tex> превратится в <tex>B_{k-1}</tex>. | |
− | Для любого узла <tex>x</tex> в дереве <tex>T_k</tex> обозначим: <tex>Degree(x)</tex> {{---}} количество детей узла <tex>x</tex> | + | {{Утверждение |
+ | |id=about_thin_tree_rank. | ||
+ | |statement=Ранг тонкого дерева равен количеству детей его корня. | ||
+ | }} | ||
+ | |||
+ | Для любого узла <tex>x</tex> в дереве <tex>T_k</tex> обозначим: | ||
+ | * <tex>Degree(x)</tex> {{---}} количество детей узла <tex>x</tex>. | ||
+ | * <tex>Rank(x)</tex> {{---}} ранг соответствующего узла в биномиальном дереве <tex>B_k</tex>. | ||
== Свойства тонкого дерева == | == Свойства тонкого дерева == | ||
{{Утверждение | {{Утверждение | ||
|id=about_thin_tree. | |id=about_thin_tree. | ||
− | |statement=Тонкое дерево обладает следующими | + | |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>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> ранги его детей от самого правого к самому левому равны соответственно <tex>0,1,2,...,Degree(x)-1</tex>. | ||
− | # Узел <tex>x</tex> помечен тогда и только тогда, | + | # Узел <tex>x</tex> помечен тогда и только тогда, когда его ранг на 2 больше, чем ранг его самого левого сына, или его ранг равен 1, и он не имеет детей. |
}} | }} | ||
Строка 61: | Строка 68: | ||
== Представление тонкой кучи == | == Представление тонкой кучи == | ||
− | Поскольку при работе с ''тонкой кучей'' ссылка на родителя требуется только у самого левого | + | ''Тонкую кучу'' можно представить как односвязный список корней ''тонких деревьев'', причем корень с минимальным ключом должен быть первым в списке. |
+ | |||
+ | Поскольку при работе с ''тонкой кучей'' ссылка на родителя требуется только у самого левого его ребенка, можно хранить ее вместо ссылки на левого брата этой вершины. | ||
Таким образом, для эффективной работы ''тонкой кучи'' необходимы следующие поля узла: | Таким образом, для эффективной работы ''тонкой кучи'' необходимы следующие поля узла: | ||
Строка 67: | Строка 76: | ||
*<tex>child</tex> {{---}} указатель на самого левого ребенка узла; | *<tex>child</tex> {{---}} указатель на самого левого ребенка узла; | ||
*<tex>right</tex> {{---}} указатель на правого брата узла, либо на следующий корень, если текущий узел корень; | *<tex>right</tex> {{---}} указатель на правого брата узла, либо на следующий корень, если текущий узел корень; | ||
− | *<tex>left</tex> {{---}} указатель на левого брата узла, либо на родителя, если текущий узел самый левый, либо null, если это | + | *<tex>left</tex> {{---}} указатель на левого брата узла, либо на родителя, если текущий узел самый левый, либо null, если это корень; |
*<tex>rank</tex> {{---}} ранг узла (количество дочерних узлов данного узла). | *<tex>rank</tex> {{---}} ранг узла (количество дочерних узлов данного узла). | ||
− | |||
− | |||
Для ускорения проверки на ''тонкость'' (''thinness'') можно отдельно хранить помеченность вершины. | Для ускорения проверки на ''тонкость'' (''thinness'') можно отдельно хранить помеченность вершины. | ||
+ | Также в вершине можно хранить любую дополнительную информацию. | ||
− | + | Для работы с ''тонкой кучей'' достаточно иметь ссылку на ее первый корень. | |
== Операции над тонкой кучей == | == Операции над тонкой кучей == | ||
− | Рассмотрим операции, которые можно производить над тонкой кучей. Время работы указано в таблице: | + | Рассмотрим операции, которые можно производить над ''тонкой кучей''. Время работы указано в таблице: |
{| border="1" | {| border="1" | ||
|-align="center" | |-align="center" | ||
Строка 103: | Строка 111: | ||
|} | |} | ||
− | Многие операции над тонкой кучей выполняются так же, как и над фиббоначиевой. | + | Многие операции над ''тонкой кучей'' выполняются так же, как и над ''фиббоначиевой''. |
Для амортизационного анализа применим метод потенциалов. | Для амортизационного анализа применим метод потенциалов. | ||
Строка 113: | Строка 121: | ||
|statement=Определённый таким образом потенциал обладает свойствами: | |statement=Определённый таким образом потенциал обладает свойствами: | ||
# <tex>\Phi \geqslant 0</tex>. | # <tex>\Phi \geqslant 0</tex>. | ||
− | # Для пустой кучи <tex>\Phi = 0</tex>. | + | # Для пустой ''тонкой кучи'' <tex>\Phi = 0</tex>. |
}} | }} | ||
Строка 119: | Строка 127: | ||
=== makeHeap === | === makeHeap === | ||
− | Возвращаем новый пустой корневой список, его потенциал <tex>\Phi=0</tex>. | + | Возвращаем ссылку на новый пустой корневой список, его потенциал <tex>\Phi=0</tex>. |
Стоимость <tex>O(1)</tex>. | Стоимость <tex>O(1)</tex>. | ||
Строка 137: | Строка 145: | ||
=== meld === | === meld === | ||
− | Сливаем корневые списки, ставя первым тот список, | + | Сливаем корневые списки, ставя первым тот список, у которого ключ первого корня минимален. Суммарный потенциал <tex>\Phi</tex> не меняется. |
Стоимость <tex>O(1)</tex>. | Стоимость <tex>O(1)</tex>. | ||
Строка 147: | Строка 155: | ||
# Cливаем детей с корневым списком. | # Cливаем детей с корневым списком. | ||
# Объединяем, пока возможно, тонкие деревья одного ранга. | # Объединяем, пока возможно, тонкие деревья одного ранга. | ||
− | Это можно сделать, например, с помощью вспомогательного массива размером <tex>O(D(n))</tex>, в <tex>i</tex>-ой ячейке | + | Это можно сделать, например, с помощью вспомогательного массива размером <tex>O(D(n))</tex>, в <tex>i</tex>-ой ячейке которого хранится корень тонкого дерева <tex>T_i</tex> ранга <tex>i</tex>. |
Изначально массив пуст, а мы добавляем в него все деревья нашего корневого списка. | Изначально массив пуст, а мы добавляем в него все деревья нашего корневого списка. | ||
− | При добавлении нового дерева мы, | + | При добавлении нового дерева мы, если дерево такого ранга уже есть в массиве, связываем его с существующим и пытаемся добавить новое дерево с рангом на <tex>1</tex> больше. |
− | Пусть мы сделали <tex>ls</tex> | + | Пусть мы сделали <tex>ls</tex> связывающих шагов (''linking steps'') во время добавления в массив. |
− | Мы удалили корень из списка за <tex>O(1)</tex>, затем за <tex>O(D(n))</tex> нормализовали детей корня и добавили в корневой список, | + | Мы удалили корень из списка за <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>. | Получили фактическую стоимость <tex>O(D(n))+O(ls)</tex>. С другой стороны, при добавлении детей в список мы увеличили потенциал <tex>\Phi</tex> не более чем на <tex>O(D(n))</tex>, а каждый связывающий шаг уменьшает наш потенциал <tex>\Phi</tex> на <tex>1</tex>. |
Версия 10:51, 24 мая 2013
Тонкая куча — это структура данных, реализующая приоритетную очередь с теми же асимптотическими оценками, что и фиббоначиева куча, но имеющая большую практическую ценность из-за меньших констант.
Тонкие кучи, как и многие другие кучеобразные структуры, аналогичны биномиальным кучам.
Содержание
Тонкое дерево
Определение: |
Тонкое дерево (thin tree) | ранга — это дерево, которое может быть получено из биномиального дерева удалением у нескольких внутренних, то есть не являющихся корнем или листом, узлов самого левого сына.
Ограничение на принадлежность внутренним узлам вызвано тем, что у листьев детей нет, а если у корня удалить самого левого сына, то превратится в .
Утверждение: |
Ранг тонкого дерева равен количеству детей его корня. |
Для любого узла
в дереве обозначим:- — количество детей узла .
- — ранг соответствующего узла в биномиальном дереве .
Свойства тонкого дерева
Утверждение: |
Тонкое дерево обладает следующими свойствами:
|
Тонкая куча
Определение: |
Тонкий лес (thin forest) — это набор тонких деревьев, ранги которых не обязательно попарно различны. |
Утверждение: |
Для любого натурального числа существует тонкий лес, который содержит ровно элементов и состоит из тонких деревьев попарно различных рангов. |
Действительно, любой биномиальный лес является тонким, а для биномиального леса рассматриваемое утверждение справедливо. |
Определение: |
Тонкая куча (thin heap) — это кучеобразно нагруженный тонкий лес. |
Пусть — максимально возможный ранг узла в тонкой куче, содержащей элементов.
Теорема (О максимальном ранге узла): |
В тонкой куче из элементов , где — золотое сечение. |
Доказательство: |
Сначала покажем, что узел ранга в тонком дереве имеет не менее потомков, включая самого себя, где — -е число Фибоначчи, определяемое соотношениями , , для .Действительно, пусть — минимально возможное число узлов, включая самого себя, в тонком дереве ранга . По свойствам и тонкого дерева получаем следующие соотношения:для Числа Фибоначчи удовлетворяют этому же рекуррентному соотношению, причем неравенство можно заменить равенством. Отсюда по индукции следует, что для любых . Неравенство хорошо известно.Теперь убедимся в том, что максимально возможный ранг Отсюда следует, что тонкого дерева в тонкой куче, содержащей элементов, не превосходит числа . Действительно, выберем в тонкой куче дерево максимального ранга. Пусть — количество вершин в этом дереве, тогда . . |
Представление тонкой кучи
Тонкую кучу можно представить как односвязный список корней тонких деревьев, причем корень с минимальным ключом должен быть первым в списке.
Поскольку при работе с тонкой кучей ссылка на родителя требуется только у самого левого его ребенка, можно хранить ее вместо ссылки на левого брата этой вершины.
Таким образом, для эффективной работы тонкой кучи необходимы следующие поля узла:
- — ключ (вес) элемента;
- — указатель на самого левого ребенка узла;
- — указатель на правого брата узла, либо на следующий корень, если текущий узел корень;
- — указатель на левого брата узла, либо на родителя, если текущий узел самый левый, либо null, если это корень;
- — ранг узла (количество дочерних узлов данного узла).
Для ускорения проверки на тонкость (thinness) можно отдельно хранить помеченность вершины. Также в вершине можно хранить любую дополнительную информацию.
Для работы с тонкой кучей достаточно иметь ссылку на ее первый корень.
Операции над тонкой кучей
Рассмотрим операции, которые можно производить над тонкой кучей. Время работы указано в таблице:
Многие операции над тонкой кучей выполняются так же, как и над фиббоначиевой.
Для амортизационного анализа применим метод потенциалов.
Пусть функция потенциала определена как
где — это количество тонких деревьев в куче, а — это количество помеченных вершин.Утверждение: |
Определённый таким образом потенциал обладает свойствами:
|
makeHeap
Возвращаем ссылку на новый пустой корневой список, его потенциал
.Стоимость
.insert
Создаем новое тонкое дерево из единственного узла с ключом
, добавляем в корневой список на первое место, если ключ минимален, иначе на второе. Потенциал увеличивается на 1.Стоимость
.getMin
Обращаемся к первому корневому узлу списка, потенциал
не меняется.Стоимость
.meld
Сливаем корневые списки, ставя первым тот список, у которого ключ первого корня минимален. Суммарный потенциал
не меняется.Стоимость
.extractMin
- Удаляем корень с минимальным ключом из корневого списка.
- Для всех его помеченных детей уменьшаем ранг и делаем их нормальными.
- Cливаем детей с корневым списком.
- Объединяем, пока возможно, тонкие деревья одного ранга.
Это можно сделать, например, с помощью вспомогательного массива размером
, в -ой ячейке которого хранится корень тонкого дерева ранга .Изначально массив пуст, а мы добавляем в него все деревья нашего корневого списка.
При добавлении нового дерева мы, если дерево такого ранга уже есть в массиве, связываем его с существующим и пытаемся добавить новое дерево с рангом на
больше.Пусть мы сделали
связывающих шагов (linking steps) во время добавления в массив.Мы удалили корень из списка за
, затем за нормализовали детей корня и добавили в корневой список, далее за получили новый корневой список, в котором за нашли минимальный корень и подвесили список за него.Получили фактическую стоимость
. С другой стороны, при добавлении детей в список мы увеличили потенциал не более чем на , а каждый связывающий шаг уменьшает наш потенциал на .Cтоимость
.decreaseKey
Стоимость
.delete
Сначала выполняем
этого элемента до , затем выполняем .Стоимость
.