Фибоначчиева куча — различия между версиями
Warrior (обсуждение | вклад) (→Операции) |
Warrior (обсуждение | вклад) |
||
Строка 132: | Строка 132: | ||
Циклический двусвязный список обладает двумя преимуществами для использования в фибоначчиевых кучах. Во-первых, удаление элемента из такого списка выполняется за время <tex>O(1)</tex>. Во-вторых, если имеется два таких списка, их легко объединить в один за то время <tex>O(1)</tex>. | Циклический двусвязный список обладает двумя преимуществами для использования в фибоначчиевых кучах. Во-первых, удаление элемента из такого списка выполняется за время <tex>O(1)</tex>. Во-вторых, если имеется два таких списка, их легко объединить в один за то время <tex>O(1)</tex>. | ||
+ | |||
+ | == Потенциал == | ||
+ | |||
+ | Для анализа производительности операций введем потенциал для фибоначчиевой кучи <tex>H</tex> как <tex> \Phi(H) = t[H] + 2m[H] </tex>, где <tex> t[H] </tex> {{---}} количество элементов в корневом списке кучи, а <tex> m[H] </tex> {{---}} количество вершин, у которых удален один ребенок (то есть вершин с пометкой <tex> mark[x] == true </tex>). На языке метода предоплаты это выглядит следующим образом: возле каждого корня лежит одна монета, а возле каждой вершины, у которой удалили ребенка, лежит две монеты. | ||
== Операции == | == Операции == | ||
Строка 160: | Строка 164: | ||
Стоит заметить, что структура фибоначчиевых куч, также как биномиальных и бинарных, не могут обеспечить эффективную реализацию поиска элемента с заданным ключом, поэтому операции <tex>decreaseKey</tex> и <tex>delete</tex> получают в качестве аргумента указатель на узел, а не значение его ключа. | Стоит заметить, что структура фибоначчиевых куч, также как биномиальных и бинарных, не могут обеспечить эффективную реализацию поиска элемента с заданным ключом, поэтому операции <tex>decreaseKey</tex> и <tex>delete</tex> получают в качестве аргумента указатель на узел, а не значение его ключа. | ||
− | == | + | === makeHeap === |
+ | |||
+ | Создается новый пустой корневой список, в <tex> min[H] </tex> устанавливается значение <tex> null </tex>. Реальное время работы {{---}} <tex> O(1) </tex>. | ||
− | + | === insert === | |
− | + | Вставка элемента в фибоначчиеву кучу также тривиальна: создается новая куча из одного элемента и сливается с текущей. Амортизированная стоимость операции: 1 (создание кучи) + 2 (слияние куч + релаксация минимума) + 1(изменение потенциала) = 4. | |
− | + | === getMin === | |
− | == | + | === merge === |
Слияние двух фибоначчиевых куч происходит просто: объединяем списки этих куч в один, релаксируем минимум. Реальное время работы {{---}} <tex> O(1) </tex>. Амортизированное время работы - также <tex> O(1) </tex>, поскольку, при объединении двух куч в одну, потенциалы обеих куч суммируются, итоговая сумма потенциалов не изменяется, <tex> \Phi_{n + 1} - \Phi_n = 0 </tex>. | Слияние двух фибоначчиевых куч происходит просто: объединяем списки этих куч в один, релаксируем минимум. Реальное время работы {{---}} <tex> O(1) </tex>. Амортизированное время работы - также <tex> O(1) </tex>, поскольку, при объединении двух куч в одну, потенциалы обеих куч суммируются, итоговая сумма потенциалов не изменяется, <tex> \Phi_{n + 1} - \Phi_n = 0 </tex>. | ||
− | |||
− | |||
− | == | + | === extractMin === |
− | Первая рассматриваемая операция, в ходе которой меняется структура кучи. Здесь используется вспомогательная процедура Consolidate ("уплотнение" кучи). Возьмем указатель на <tex> min[H] </tex>, удалим эту вершину. Ее поддеревья (их не более, чем <tex> D[H] </tex>, где <tex> D[H] </tex> {{---}} максимальная степень вершины в куче) все положим в корневой список. Теперь вызываем процедуру <tex> | + | Первая рассматриваемая операция, в ходе которой меняется структура кучи. Здесь используется вспомогательная процедура Consolidate ("уплотнение" кучи). Возьмем указатель на <tex> min[H] </tex>, удалим эту вершину. Ее поддеревья (их не более, чем <tex> D[H] </tex>, где <tex> D[H] </tex> {{---}} максимальная степень вершины в куче) все положим в корневой список. Теперь вызываем процедуру <tex> consolidate </tex>. |
− | === | + | ==== consolidate ==== |
Данная процедура принимает кучу, и делает из нее кучу, в корневом списке которой <tex> O(D[H]) </tex> вершин. | Данная процедура принимает кучу, и делает из нее кучу, в корневом списке которой <tex> O(D[H]) </tex> вершин. | ||
Строка 194: | Строка 198: | ||
На языке метода предоплаты: Положим у каждой вершины-ребенка удаленной монету. Это <tex> O(D[H]) </tex> действий. Теперь: у каждой вершины в корневом списке лежит монета, потратим ее на то, чтобы провести процедуру <tex> Consolidate </tex>. Получили новый корневой список, снова раздаем монеты каждой вершине. Итого <tex> O(D[H]) + O(D[H]) = O(D[H]) </tex> действий. | На языке метода предоплаты: Положим у каждой вершины-ребенка удаленной монету. Это <tex> O(D[H]) </tex> действий. Теперь: у каждой вершины в корневом списке лежит монета, потратим ее на то, чтобы провести процедуру <tex> Consolidate </tex>. Получили новый корневой список, снова раздаем монеты каждой вершине. Итого <tex> O(D[H]) + O(D[H]) = O(D[H]) </tex> действий. | ||
− | == | + | === decreaseKey === |
Основная идея: хотим, чтобы учетная стоимость данной операции была <tex> O(1) </tex>. Было бы хорошо, чтобы вершина не всплывала до корня; тогда дерево не придется сильно перестраивать. Для этого, при удобном случае будем вырезать поддерево полностью и перемещать его в корневой список. Итак, сам алгоритм: | Основная идея: хотим, чтобы учетная стоимость данной операции была <tex> O(1) </tex>. Было бы хорошо, чтобы вершина не всплывала до корня; тогда дерево не придется сильно перестраивать. Для этого, при удобном случае будем вырезать поддерево полностью и перемещать его в корневой список. Итак, сам алгоритм: | ||
Строка 201: | Строка 205: | ||
# Иначе, вырезаем дерево с текущей вершиной в корневой список, и производим каскадное вырезание родителя. | # Иначе, вырезаем дерево с текущей вершиной в корневой список, и производим каскадное вырезание родителя. | ||
− | === | + | ==== cut ==== |
При вырезании вершины мы удаляем ее из списка детей своего родителя, уменьшаем степень ее родителя (<tex> degree[p[x]] </tex>) и снимаем пометку с текущей вершины (<tex> mark[x] = false </tex>). | При вырезании вершины мы удаляем ее из списка детей своего родителя, уменьшаем степень ее родителя (<tex> degree[p[x]] </tex>) и снимаем пометку с текущей вершины (<tex> mark[x] = false </tex>). | ||
− | === | + | ==== cascadingCut ==== |
[[File:Cascading-cut.png|thumb|600px|Каскадное вырезание]] | [[File:Cascading-cut.png|thumb|600px|Каскадное вырезание]] | ||
Строка 221: | Строка 225: | ||
На рисунке проиллюстрирован процесс понижения ключа вершины c 10 до 7. Серым помечены вершины с <tex> mark[x] == true </tex>. | На рисунке проиллюстрирован процесс понижения ключа вершины c 10 до 7. Серым помечены вершины с <tex> mark[x] == true </tex>. | ||
− | == | + | === delete === |
Удаление вершины реализуется через уменьшение ее ключа до <tex> -\infty </tex> и последующим извлечением минимума. Амортизированное время работы: <tex> O(1) + O(D[H]) = O(D[H]) </tex>. | Удаление вершины реализуется через уменьшение ее ключа до <tex> -\infty </tex> и последующим извлечением минимума. Амортизированное время работы: <tex> O(1) + O(D[H]) = O(D[H]) </tex>. |
Версия 16:04, 9 марта 2012
Содержание
Фибоначчиевы деревья
Определение: |
Фибоначчиево дерево - биномиальное дерево, где у каждой вершины удалено не более одного ребенка. |
Определение: |
Фибоначчиево дерево порядка | - биномиальное дерево порядка , из которого оно получено.
Лемма: |
Для всех целых
, где — число Фибоначчи, определяемое формулой: |
Доказательство: |
Докажем лемму по индукции: при , что действительно верно. По индукции предполагаем, что . Тогда |
Лемма: |
Фибоначчиево дерево порядка содержит не менее вершин. |
Доказательство: |
Докажем это утверждение по индукции. Пусть — минимальный размер фибоначчиева дерева порядка n.При . При . Предположим по индукции, что для всех . Пусть в нашем дереве удалено поддерево порядка . ТогдаНо по предыдущей лемме . Следовательно, |
Фибоначчиевы кучи
Определение: |
Фибоначчиева куча - набор фибоначчиевых деревьев, упорядоченных в соответствии со свойством неубывающей кучи. |
Фибоначчиевы кучи поддерживают тот же набор операций, что и биномиальные кучи, но имеют то преимущество, что операции, в которых не требуется удаление, имеют амортизированное время работы, равное .
С теоретической точки зрения фибоначчиевы кучи особенно полезны в случае, когда количество операций бинарные кучи.
и относительно мало по сравнению с количеством других операций. Однако с практической точки зрения программная сложность и высокие значения постоянных множителей в формулах времени работы существенно снижают эффективность применения фибоначчиевых куч, делая их в большинстве случаев менее привлекательными, чем обычные
Определение: |
Степень вершины - порядок фибоначчиева дерева, чьим корнем эта вершина является. |
Лемма: |
, где |
Доказательство: |
Для начала докажем, что Используем для этого математическую индукцию. При , что верно. При , что также верно. По индукции предполагаем, что и . Тогда
Подставив вместо Поскольку его значение, нетрудно убедится, что , то выполняются неравенства . Таким образом, -е число Фибоначчи равно , округленному до ближайшего целого числа. Следовательно, . |
Лемма: |
Максимальная степень произвольной вершины в фибоначчиевой куче с вершинами равна |
Доказательство: |
Пусть доказанному выше в дереве, корень которого , содержится не менее вершин, что в свою очередь по лемме равно . То есть — произвольная вершина в фибоначчиевой куче с вершинами, и пусть — степень вершины . Тогда по
Логарифмируя по основанию , получаемТаким образом, максимальная степень произвольной вершины равна . |
Структура
- Каждый узел
- — поле, в котором хранится ключ;
- — указатель на родительский узел;
- — указатель на один из дочерних узлов;
- — указатель на левый сестринский узел;
- — указатель на правый сестринский узел;
- — поле, в котором хранится количество дочерних узлов;
- — логическое значение, которое указывает, были ли потери узлом дочерних узлов, начиная с момента, когда стал дочерним узлом какого-то другого узла.
в куче содержит следующие указатели и поля:
- Дочерние узлы объединены при помощи указателей и в циклический двусвязный список.
- Корни всех деревьев в связаны при помощи указателей и в циклический двусвязный список корней.
- Обращение к выполняется посредством указателя на корень дерева с минимальным ключом. Этот узел называется минимальным узлом .
- Текущее количество узлов в хранится в .
Циклический двусвязный список обладает двумя преимуществами для использования в фибоначчиевых кучах. Во-первых, удаление элемента из такого списка выполняется за время
. Во-вторых, если имеется два таких списка, их легко объединить в один за то время .Потенциал
Для анализа производительности операций введем потенциал для фибоначчиевой кучи
как , где — количество элементов в корневом списке кучи, а — количество вершин, у которых удален один ребенок (то есть вершин с пометкой ). На языке метода предоплаты это выглядит следующим образом: возле каждого корня лежит одна монета, а возле каждой вершины, у которой удалили ребенка, лежит две монеты.Операции
Рассмотрим операции, которые поддерживают фибоначчиевы кучи. Амортизированное время их работы показано в таблице.
Стоит заметить, что структура фибоначчиевых куч, также как биномиальных и бинарных, не могут обеспечить эффективную реализацию поиска элемента с заданным ключом, поэтому операции
и получают в качестве аргумента указатель на узел, а не значение его ключа.makeHeap
Создается новый пустой корневой список, в
устанавливается значение . Реальное время работы — .insert
Вставка элемента в фибоначчиеву кучу также тривиальна: создается новая куча из одного элемента и сливается с текущей. Амортизированная стоимость операции: 1 (создание кучи) + 2 (слияние куч + релаксация минимума) + 1(изменение потенциала) = 4.
getMin
merge
Слияние двух фибоначчиевых куч происходит просто: объединяем списки этих куч в один, релаксируем минимум. Реальное время работы —
. Амортизированное время работы - также , поскольку, при объединении двух куч в одну, потенциалы обеих куч суммируются, итоговая сумма потенциалов не изменяется, .
extractMin
Первая рассматриваемая операция, в ходе которой меняется структура кучи. Здесь используется вспомогательная процедура Consolidate ("уплотнение" кучи). Возьмем указатель на
, удалим эту вершину. Ее поддеревья (их не более, чем , где — максимальная степень вершины в куче) все положим в корневой список. Теперь вызываем процедуру .consolidate
Данная процедура принимает кучу, и делает из нее кучу, в корневом списке которой
вершин.Для этого возьмем массив списков указателей на корни деревьев
, где — максимальная степень вершины в текущем корневом списке. Далее мы увидим, что .Затем происходит процесс, аналогичный слиянию биномиальных куч : добавляем поочередно каждый корень, смотря на его степень. Пусть она равна . Если в соответствующей ячейке A еще нету вершины, записываем текущую вершину туда. Иначе подвешиваем одно дерево к другому, и пытаемся также добавить дерево, степень корня которого уже равна . Продолжаем, пока не найдем свободную ячейку.
Учетная стоимость
равна . Докажем это:Пусть изначально в корневом списке было
вершин. Тогда в ходе операции мы сделали слияний деревьев. Но эти слияний скомпенсируются уменьшением потенциала . Остальных действий будет также . Таким образом, учетная стоимость .На языке метода предоплаты: Положим у каждой вершины-ребенка удаленной монету. Это
действий. Теперь: у каждой вершины в корневом списке лежит монета, потратим ее на то, чтобы провести процедуру . Получили новый корневой список, снова раздаем монеты каждой вершине. Итого действий.decreaseKey
Основная идея: хотим, чтобы учетная стоимость данной операции была
. Было бы хорошо, чтобы вершина не всплывала до корня; тогда дерево не придется сильно перестраивать. Для этого, при удобном случае будем вырезать поддерево полностью и перемещать его в корневой список. Итак, сам алгоритм:- Проверяем, если новое значение ключа все же не меньше значения ключа родителя, то все хорошо, и мы выходим.
- Иначе, вырезаем дерево с текущей вершиной в корневой список, и производим каскадное вырезание родителя.
cut
При вырезании вершины мы удаляем ее из списка детей своего родителя, уменьшаем степень ее родителя (
) и снимаем пометку с текущей вершины ( ).cascadingCut
Перед вызовом каскадного вырезания нам известно, что перед этим мы удалили ребенка у этой вершины. Если
, то мы ставим эту пометку и заканчиваем. В противном случае, вырезаем текущую вершину, и запускаем каскадное вырезание от родителя.Докажем, что амортизированное время работы операции "уменьшение ключа" есть
. Поскольку в процедуре нет циклов, ее время работы определяется лишь количеством рекурсивных вызовов каскадного вырезания.Пусть мы вызвали процедуру каскадного вырезания
раз. Тогда вершин с пометкой стало на меньше, а в корневом списке прибавилось новых вершин. Итого, время работы будет: . Теперь, подбирая соответствующую константу в потенциале, можем добиться того, чтобы амортизированное время работы этой процедуры стало . Теперь также стало ясно, для чего в определении нашего потенциала количество вершин с пометкой учитывается вдвое больше, чем количество вершин в корневом списке.На языке метода предоплаты: Покажем, что взяв в начале 4 монеты, нам хватит этого для выполнения данной операции. Возьмем 4 монеты перед началом уменьшения ключа. Теперь 1 монету потратим на перенос в корневой список и релаксацию минимума, еще 1 - на то, чтобы положить монету у новой вершины в корневом списке. У нас осталось 2 монеты. Далее производим каскадное вырезание: в случае, когда
, кладем 2 монеты к этой вершине, и устанавливаем соответствующую пометку. Инвариант сохраняется.Иначе,
и там лежит 2 монеты. 2 + 2 = 4, и мы можем рекурсивно продолжить данный процесс. Оценка доказана.На рисунке проиллюстрирован процесс понижения ключа вершины c 10 до 7. Серым помечены вершины с
.delete
Удаление вершины реализуется через уменьшение ее ключа до
и последующим извлечением минимума. Амортизированное время работы: .Поскольку, ранее мы показали, что
, то соответствующие оценки доказаны.Ссылки
- Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн - Алгоритмы: построение и анализ. — М.: Издательский дом «Вильямс», 2005. — С. 1296. — ISBN 5-8459-0857-4
- http://ru.wikipedia.org/wiki/Фибоначчиева_куча
- http://www.intuit.ru/department/algorithms/dscm/7/2.html - INTUIT.ru
- Визуализаторы на rain.ifmo.ru: http://rain.ifmo.ru/cat/view.php/vis/heaps
- http://www.cs.duke.edu/courses/fall05/cps230/L-11.pdf