Редактирование: Толстая куча на избыточном счётчике

Перейти к: навигация, поиск

Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 5: Строка 5:
 
|neat = 1
 
|neat = 1
 
|definition=
 
|definition=
Определяем '''толстое дерево''' (англ. ''Thick tree'') <tex>F_k</tex> ранга <tex>k ~(k = 0, 1, 2 ,\ldots )</tex> следующим образом:
+
Определяем '''толстое дерево''' (англ. ''Fat Heap'') <tex>F_k</tex> ранга <tex>k ~(k = 0, 1, 2 ~\dots )</tex> следующим образом:
 
*Толстое дерево <tex>F_0</tex> ранга ноль состоит из единственного узла. <br>
 
*Толстое дерево <tex>F_0</tex> ранга ноль состоит из единственного узла. <br>
*Толстое дерево <tex>F_k</tex>  ранга <tex>k</tex>, для <tex>k = 1, 2, 3,\ldots </tex>, состоит из трех деревьев  <tex>F_{k-1}</tex> ранга <tex>k-1</tex>,таких, что корни двух из них являются самыми левыми потомками корня третьего.
+
*Толстое дерево <tex>F_k</tex>  ранга <tex>k</tex>, для <tex>k = 1, 2, 3,\dots </tex>, состоит из трех деревьев  <tex>F_{k-1}</tex> ранга <tex>k</tex>,таких, что корни двух из них являются самыми левыми потомками корня третьего.
 +
Ранг узла <tex>x</tex> в толстом дереве определяется как ранг толстого поддерева с корнем в узле <tex>x</tex>.  
 
}}
 
}}
[[Файл:FatHeap.png |400px|thumb|center|Пример толстых деревьев <tex>F_0, F_1, F_2, F_3</tex>]]
+
[[Файл:FatTreesExample.png |400px|thumb|left|Пример толстых деревьев <tex>F_0, F_1, F_2, F_3</tex>]]
  
{{Определение
 
|id=def3
 
|neat =
 
|definition=
 
'''Ранг узла''' <tex>x</tex> в толстом дереве определяется как ранг толстого поддерева с корнем в узле <tex>x</tex>.
 
}}
 
 
===Свойства толстых деревьев===
 
  
 
{{Утверждение
 
{{Утверждение
Строка 25: Строка 18:
 
|about=
 
|about=
 
|statement=
 
|statement=
В толстом дереве ранга <tex>k</tex> ровно <tex>3^k</tex> узлов.
+
'''Свойства толстых деревьев:''' <br>
 +
*В толстом дереве ранга <tex>k</tex> ровно <tex>3^k</tex> узлов.  <br>
 +
*Для любого натурального числа <tex>n</tex> существует лес из толстых деревьев, в котором ровно <tex>n</tex> узлов. Такой лес можно построить, включив в него столько деревьев ранга <tex>i</tex>, каково значение  <tex>i</tex>-го разряда представления числа <tex>n</tex> в троичной системе счисления. Заметим, что для построения такого леса можно использовать и избыточные троичные представления.
 +
*Толстый лес из <tex>n</tex> узлов содержит <tex>O(n\log{n})</tex> деревьев.
 
|proof=
 
|proof=
}}
 
{{Определение
 
|id=thin_forest_def
 
|definition='''Толстый лес''' (англ. ''Thick forest'') {{---}} это набор толстых деревьев, ранги которых не обязательно попарно различны.
 
}}
 
 
{{Утверждение
 
|id=proposal1.
 
|author=
 
|about=
 
|statement=
 
Для любого натурального числа <tex>n</tex> существует лес из толстых деревьев, в котором ровно <tex>n</tex> узлов.
 
|proof= Действительно, такой лес можно построить, включив в него столько деревьев ранга <tex>i</tex>, каково значение  <tex>i</tex>-го разряда представления числа <tex>n</tex> в троичной системе счисления. Заметим, что для построения такого леса можно использовать и избыточные троичные представления.
 
 
}}
 
}}
  
{{Утверждение
 
|id=proposal1.
 
|author=
 
|about=
 
|statement=
 
Толстый лес из <tex>n</tex> узлов содержит <tex>O(n\log{n})</tex> деревьев.
 
|proof=
 
}}
 
 
==Толстые кучи==
 
 
{{Определение
 
{{Определение
 
|id=def2  
 
|id=def2  
Строка 70: Строка 43:
 
'''Нагруженный лес''' назовем '''почти кучеобразным''', если для каждого значения <tex>k</tex> в нем имеется не более двух '''неправильных''' узлов ранга <tex>k</tex>.
 
'''Нагруженный лес''' назовем '''почти кучеобразным''', если для каждого значения <tex>k</tex> в нем имеется не более двух '''неправильных''' узлов ранга <tex>k</tex>.
 
}}
 
}}
Здесь и далее ''Толстая куча на избыточном счетчике'' будет заменено на более лаконичное ''Толстая куча''.
+
 
 +
==Толстые кучи==
 +
 
 +
Здесь и далее "Толстая куча на избыточном счетчике" будет заменено на более лаконичное "Толстая куча".
 
{{Определение
 
{{Определение
 
|id=def5  
 
|id=def5  
 
|neat =
 
|neat =
 
|definition=
 
|definition=
'''Толстая куча''' (англ. ''Thick heap, Fat heap'') — это '''почти кучеобразный''' '''нагруженный лес'''.
+
'''Толстая куча''' — это '''почти кучеобразный''' '''нагруженный лес'''.
 
}}
 
}}
  
=== Структура узла ===
+
===Представление толстой кучи===
 
Каждый узел толстой кучи будем представлять записью со следующими полями:
 
Каждый узел толстой кучи будем представлять записью со следующими полями:
'''struct''' Node
+
*<tex>key</tex> {{---}} ключ элемента, приписанного узлу дерева
  '''int''' key <span style="color:#008000">     // ключ элемента, приписанного узлу дерева</span>
+
*<tex>parent</tex> {{---}} указатель на родителя
  '''Node''' parent <span style="color:#008000"> // указатель на родителя узла</span>
+
*<tex>left</tex> {{---}} указатель на ближайшего левого брата
  '''Node''' left <span style="color:#008000">   // указатель на ближайшего левого брата узла</span>
+
*<tex>right</tex> {{---}} указатель на ближайшего правого брата
  '''Node''' right <span style="color:#008000">   // указатель на ближайшего правого брата узла</span>
+
*<tex>lChild</tex> {{---}} указатель на самого левого сына
  '''Node''' lChild <span style="color:#008000"> // указатель на самого левого сына</span>
+
*<tex>rank</tex> {{---}} ранг узла.
  '''int''' rank <span style="color:#008000">     // ранг узла</span>
 
  
 
"Братья" (узлы корневых деревьев, а также сыновья каждого узла) объединены в двусвязный список при помощи указателей <tex>left</tex> и <tex>right</tex>. У самого левого (правого) "брата" в этом списке указатель <tex>left</tex> (<tex>right</tex>) равен <tex>NULL</tex>.
 
"Братья" (узлы корневых деревьев, а также сыновья каждого узла) объединены в двусвязный список при помощи указателей <tex>left</tex> и <tex>right</tex>. У самого левого (правого) "брата" в этом списке указатель <tex>left</tex> (<tex>right</tex>) равен <tex>NULL</tex>.
  
===Структура кучи===
+
[[Файл:FatHeapExample.png |400px|thumb|right|Представление леса списком]]
 +
 
 +
===Вспомогательные структуры===
 +
Нам понадобятся понятия '''корневого счетчика''' и '''счетчика нарушений'''.
 +
 
 
Толстую кучу будем представлять записью следующего вида:
 
Толстую кучу будем представлять записью следующего вида:
 +
<tex>fatHeap = (rootCount, countViolation, minpointer, maxRank)</tex>, где:
  
'''struct''' FatHeap
+
<tex>rootCount</tex> {{---}} массив, соответствующий '''корневому счетчику'''
  '''int[]''' rootCount <span style="color:#008000">       // массив, соответствующий корневому счетчику</span>
+
 
  '''int[]''' countViolation <span style="color:#008000"> // массив, соответствующий счетчику нарушений</span>
+
<tex>countViolation</tex> {{---}} массив, соответствующий '''счетчику нарушений'''
  '''Node''' minPointer <span style="color:#008000">       // указатель на элемент кучи с минимальным ключом</span>
+
 
  '''int''' maxRank <span style="color:#008000">          // наибольший ранг среди рангов деревьев, присутствующих в куче</span>
+
<tex>minPointer</tex> {{---}} указатель на элемент кучи с '''минимальным ключом'''
[[Файл:FatHeapExample.png |400px|thumb|center|Представление леса списком]]
+
 
 +
<tex>maxRank</tex> {{---}} '''наибольший ранг''' среди рангов деревьев, присутствующих в куче
  
 
==Избыточное представление чисел==
 
==Избыточное представление чисел==
Строка 106: Строка 87:
 
|neat =  
 
|neat =  
 
|definition=
 
|definition=
'''Избыточным''' <tex>b</tex>-арным представлением числа <tex>x</tex> будем называть последовательность <tex>d = d_n, d_{n-1}, \ldots, d_0</tex>, такую что
+
'''Избыточным''' <tex>b</tex>-арным представлением числа <tex>x</tex> будем называть последовательность <tex>d = d_n, d_{n-1}, ... d_0</tex>, такую что
<tex dpi = "120">x = {\sum\limits^{n}_{i = 0} {d_i}}{b^i}</tex>
+
<tex dpi = "150">x = {\sum\limits^{n}_{i = 0} {d_i}}{b^i}</tex>
  
где <tex> d_i \in \{0, 1, \ldots, b\} </tex>, <tex> i \in \{0, 1, \ldots, n\} </tex>
+
где <tex> d_i \in \{0, 1, ..., b\} </tex>, <tex> i \in \{0, 1, ..., n\} </tex>
 
}}
 
}}
  
Строка 123: Строка 104:
 
|neat =  
 
|neat =  
 
|definition=
 
|definition=
Пусть  <tex>L(i)</tex> — номер разряда, отличного от  <tex>b-1</tex> и ближайшего слева от  <tex>i</tex>-го разряда в регулярном  <tex>b</tex>-арном избыточном представлении <tex>d = d_n, \ldots, d_0</tex>.
+
Пусть  <tex>L(i)</tex> — номер разряда, отличного от  <tex>b-1</tex> и ближайшего слева от  <tex>i</tex>-го разряда в регулярном  <tex>b</tex>-арном избыточном представлении <tex>d = d_n, ... d_0</tex>.
 
<br>
 
<br>
'''Прямой указатель''' <tex>L'(i)</tex> определим следующим образом:  
+
Определим <tex>L'(i)</tex>  следующим образом:  
 
*<tex>L'(i) = L(i)</tex> , если <tex>d_i \in \{b-1, b-2\}</tex> и <tex>d(L(i))=b</tex>;
 
*<tex>L'(i) = L(i)</tex> , если <tex>d_i \in \{b-1, b-2\}</tex> и <tex>d(L(i))=b</tex>;
 
*<tex>L'(i)</tex> — произвольное число <tex>>i</tex>, если <tex>d_i \in \{b-1, b-2\}</tex> и <tex>d(L(i))<b-1</tex>;  
 
*<tex>L'(i)</tex> — произвольное число <tex>>i</tex>, если <tex>d_i \in \{b-1, b-2\}</tex> и <tex>d(L(i))<b-1</tex>;  
 
*<tex>L'(i)</tex>  — не определено, если <tex>d \notin \{b-1, b-2 \}</tex> .
 
*<tex>L'(i)</tex>  — не определено, если <tex>d \notin \{b-1, b-2 \}</tex> .
 +
<br>
 +
Величину  <tex>L'(i)</tex> будем называть прямым указателем.
 
}}
 
}}
  
 
===Фиксация цифры===
 
===Фиксация цифры===
 
Фиксацией цифры <tex>b</tex>, стоящей в <tex>i</tex>-м разряде представления <tex>d</tex>, назовем операцию <tex>\mathrm{fix(i)}</tex>, заключающуюся в обнулении цифры <tex>d_i</tex>  и инкрементировании цифры <tex> d_{i+1} </tex>, при этом если <tex>i=n</tex> , то полагаем  <tex>d_{i+1} = 1</tex>. При каждом выполнении операции фиксации будем обновлять значение <tex>L'(i)</tex>. Очевидно, при  <tex>b>2</tex> операцию <tex>\mathrm{fix(i)}</tex>  можно выполнить следующим образом:
 
Фиксацией цифры <tex>b</tex>, стоящей в <tex>i</tex>-м разряде представления <tex>d</tex>, назовем операцию <tex>\mathrm{fix(i)}</tex>, заключающуюся в обнулении цифры <tex>d_i</tex>  и инкрементировании цифры <tex> d_{i+1} </tex>, при этом если <tex>i=n</tex> , то полагаем  <tex>d_{i+1} = 1</tex>. При каждом выполнении операции фиксации будем обновлять значение <tex>L'(i)</tex>. Очевидно, при  <tex>b>2</tex> операцию <tex>\mathrm{fix(i)}</tex>  можно выполнить следующим образом:
  '''void''' fix('''int''' i):
+
  fix('''int''' i):
 
   '''if''' d[i] == b
 
   '''if''' d[i] == b
 
     d[i] = 0
 
     d[i] = 0
Строка 144: Строка 127:
 
===Инкремент===
 
===Инкремент===
 
Операцию <tex>\mathrm{inc(i)}</tex> инкрементирования <tex>i</tex>-й цифры избыточного представления <tex>d</tex> можно выполнить так:
 
Операцию <tex>\mathrm{inc(i)}</tex> инкрементирования <tex>i</tex>-й цифры избыточного представления <tex>d</tex> можно выполнить так:
  '''void''' inc('''int''' i):
+
  inc('''int''' i):
 
   fix(i)
 
   fix(i)
 
   '''if''' (d[i] == b - 1) '''or''' (d[i] == b - 2)
 
   '''if''' (d[i] == b - 1) '''or''' (d[i] == b - 2)
Строка 154: Строка 137:
  
 
Эта схема может быть расширена для выполнения за константное время декрементирования произвольной цифры добавлением дополнительного цифрового значения <tex>b+1</tex>.
 
Эта схема может быть расширена для выполнения за константное время декрементирования произвольной цифры добавлением дополнительного цифрового значения <tex>b+1</tex>.
 +
 +
  
 
Представление приоритетной очереди основано на использовании так называемых '''избыточных счетчиков''', позволяющих за время <tex>O(1)</tex> инкрементировать любой разряд. Заметим, что использованные здесь счетчики — лишь '''один из способов реализации''' толстых куч. На самом деле, для их реализации подойдет '''произвольный d-арный''' счетчик, при условии, что трудоемкость инкрементирования любого его разряда является константной.
 
Представление приоритетной очереди основано на использовании так называемых '''избыточных счетчиков''', позволяющих за время <tex>O(1)</tex> инкрементировать любой разряд. Заметим, что использованные здесь счетчики — лишь '''один из способов реализации''' толстых куч. На самом деле, для их реализации подойдет '''произвольный d-арный''' счетчик, при условии, что трудоемкость инкрементирования любого его разряда является константной.
Строка 177: Строка 162:
 
}}
 
}}
  
'''Корневой счетчик''' представляем расширяющимся массивом <tex>\mathtt{rootCount}</tex> , каждый элемент которого — запись с тремя полями:
+
'''Корневой счетчик''' представляем расширяющимся массивом <tex>rootCount</tex> , каждый элемент которого — запись с тремя полями:
*<tex>\mathtt{rootCount[i].Value}</tex> {{---}} <tex>i</tex>-й разряд равный количеству деревьев ранга <tex>i</tex>.
+
*<tex>rootCount[i].Value</tex> {{---}} <tex>i</tex>-й разряд равный количеству деревьев ранга <tex>i</tex>.
*<tex>\mathtt{rootCount[i].forwardPointer}</tex> {{---}} прямой указатель <tex>i</tex>-го разряда.
+
*<tex>rootCount[i].forwardPointer</tex> {{---}} прямой указатель <tex>i</tex>-го разряда.
*<tex>\mathtt{rootCount[i].listPointer}</tex> {{---}} указатель на список деревьев ранга <tex>i</tex>, присутствующих в толстой куче. Деревья в этом списке связаны при помощи указателя <tex>Right</tex>  корневых узлов связываемых деревьев. Если в куче нет деревьев ранга <tex>i</tex> , то указатель <tex>\mathtt{listPointer}</tex> равен <tex>NULL</tex>.  
+
*<tex>rootCount[i].listPointer</tex> {{---}} указатель на список деревьев ранга <tex>i</tex>, присутствующих в толстой куче. Деревья в этом списке связаны при помощи указателя <tex>Right</tex>  корневых узлов связываемых деревьев. Если в куче нет деревьев ранга <tex>i</tex> , то указатель <tex>listPointer</tex> равен <tex>NULL</tex>.  
''Заметим, что если значение  равно нулю, то нам неважно значение указателя'' <tex>\mathtt{rootCount[i].listPointer}</tex>.
+
''Заметим, что если значение  равно нулю, то нам неважно значение указателя'' <tex>rootCount[i].listPointer</tex>.
  
 
===Инициализация===
 
===Инициализация===
Чтобы время инициализации счетчиков было <tex>O(1)</tex>, используем '''поразрядную''' их инициализацию. То есть будем добавлять новые разряды только тогда, когда возникает такая необходимость, и при этом инициализировать новый разряд сразу в обоих счетчиках. Для этого мы вводим переменную <tex>\mathtt{maxRank}</tex>, которая показывает нам, какая часть массивов счетчиков используется в данный момент.
+
Чтобы время инициализации счетчиков было <tex>O(1)</tex>, используем '''поразрядную''' их инициализацию. То есть будем добавлять новые разряды только тогда, когда возникает такая необходимость, и при этом инициализировать новый разряд сразу в обоих счетчиках. Для этого мы вводим переменную <tex>maxRank</tex>, которая показывает нам, какая часть массивов счетчиков используется в данный момент.
  
 
При начальной инициализации необходимо установить счетчики в состояние, которое отвечает '''пустой куче'''. Очевидно, что в пустой куче не может быть никаких нарушений.
 
При начальной инициализации необходимо установить счетчики в состояние, которое отвечает '''пустой куче'''. Очевидно, что в пустой куче не может быть никаких нарушений.
Строка 190: Строка 175:
 
===Обновление прямого указателя===
 
===Обновление прямого указателя===
 
Обновление прямого указателя <tex>i</tex>-го разряда корневого счетчика заключается в выполнении следующего псевдокода:
 
Обновление прямого указателя <tex>i</tex>-го разряда корневого счетчика заключается в выполнении следующего псевдокода:
  '''void''' updateForwardPionter('''int''' i):
+
  updateForwardPionter('''int''' i):
 
   '''if''' rootCount[i + 1].Value == 3 - 1
 
   '''if''' rootCount[i + 1].Value == 3 - 1
 
     rootCount[i].forwardPointer = rootCount[i + 1].forwardPointer
 
     rootCount[i].forwardPointer = rootCount[i + 1].forwardPointer
 
   '''else'''
 
   '''else'''
 
     rootCount[i].forwardPointer = i + 1
 
     rootCount[i].forwardPointer = i + 1
 +
  
 
===Корректировка при вставке ===
 
===Корректировка при вставке ===
Корректировка списочной части <tex>i</tex>-го разряда корневого счетчика при вставке в кучу нового дерева ранга <tex>i~(\mathrm{insertTree(i,p)})</tex>. Эта процедура вставляет новое дерево ранга  <tex>i</tex> (на него указывает указатель <tex>p</tex>) в списочную часть  <tex>i</tex>-го разряда корневого счетчика <tex>\mathtt{rootCount}</tex> выглядит так:
+
Корректировка списочной части <tex>i</tex>-го разряда корневого счетчика при вставке в кучу нового дерева ранга <tex>i~(\mathrm{insertTree(i,p)})</tex>. Эта процедура вставляет новое дерево ранга  <tex>i</tex> (на него указывает указатель <tex>p</tex>) в списочную часть  <tex>i</tex>-го разряда корневого счетчика <tex>rootCount</tex> выглядит так:
  '''void''' insertTree('''int''' i, '''Node''' p):
+
  insertTree(i, p):
 
   p1 = rootCount[i].listPointer
 
   p1 = rootCount[i].listPointer
 
   '''if''' rootCount[i].Value <tex> \ne </tex> 0
 
   '''if''' rootCount[i].Value <tex> \ne </tex> 0
Строка 208: Строка 194:
  
 
===Корректировка при удалении===
 
===Корректировка при удалении===
Корректировка списочной части <tex>i</tex>-го разряда корневого счетчика при удалении из кучи дерева ранга <tex>i~(\mathrm{deleteTree(i,p)})</tex>. Эта процедура удаляет дерево ранга <tex>i</tex> (на него указывает указатель <tex>p</tex>) из списочной части  <tex>i</tex>-го разряда корневого счетчика <tex>\mathtt{rootCount}</tex> . Будем считать, что указанное дерево присутствует в куче. Процедура заключается в выполнении следующего псевдокода:
+
Корректировка списочной части <tex>i</tex>-го разряда корневого счетчика при удалении из кучи дерева ранга <tex>i~(\mathrm{deleteTree(i,p)})</tex>. Эта процедура удаляет дерево ранга <tex>i</tex> (на него указывает указатель <tex>p</tex>) из списочной части  <tex>i</tex>-го разряда корневого счетчика <tex>rootCount</tex> . Будем считать, что указанное дерево присутствует в куче. Процедура заключается в выполнении следующего псевдокода:
  '''void''' deleteTree('''int''' i, '''Node''' p):
+
  deleteTree(i, p):
 
   p1 = rootCount[i].listPointer
 
   p1 = rootCount[i].listPointer
 
   '''if''' p1 == p
 
   '''if''' p1 == p
 
     rootCount[i].listPointer = p.right
 
     rootCount[i].listPointer = p.right
 
   j = 1
 
   j = 1
   '''while''' (j <tex> \leqslant </tex> rootCount[i].Value) '''and''' (p1.right <tex> \ne </tex> p):
+
   '''while''' (j <tex> \le </tex> rootCount[i].Value) '''and''' (p1.right <tex> \ne </tex> p):
 
     j++
 
     j++
 
     p1 = p1.right
 
     p1 = p1.right
Строка 222: Строка 208:
 
'''Связывание''' <tex>\mathrm{fastening (p1, p2, p3)}</tex> трех толстых деревьев ранга <tex>i</tex> в одно толстое дерево ранга <tex>i+1</tex>. Эта функция принимает три указателя <tex>p1, p2 ,p3</tex>  на три разных толстых дерева одного и того же ранга <tex>i</tex>  и возвращает указатель на вновь сформированное дерево ранга <tex>i+1</tex> .
 
'''Связывание''' <tex>\mathrm{fastening (p1, p2, p3)}</tex> трех толстых деревьев ранга <tex>i</tex> в одно толстое дерево ранга <tex>i+1</tex>. Эта функция принимает три указателя <tex>p1, p2 ,p3</tex>  на три разных толстых дерева одного и того же ранга <tex>i</tex>  и возвращает указатель на вновь сформированное дерево ранга <tex>i+1</tex> .
 
Процедура заключается в выполнении следующего псевдокода:
 
Процедура заключается в выполнении следующего псевдокода:
  '''Node''' fastening('''Node''' p1, '''Node''' p2, '''Node''' p3):
+
  fastening (p1, p2, p3):
   '''if''' (p1.key <tex> \leqslant </tex> p2.key) '''and''' (p1.Key <tex> \leqslant </tex> p3.key)
+
   '''if''' (p1.key <tex> \le </tex> p2.key) '''and''' (p1.Key <tex> \le </tex> p3.key)
 
     minP = p1
 
     minP = p1
 
     p1 = p2
 
     p1 = p2
 
     p2 = p3
 
     p2 = p3
   '''if''' (p2.key <tex> \leqslant </tex> p1.key) '''and''' (p2.key <tex> \leqslant </tex> p3.key)
+
   '''if''' (p2.key <tex> \le </tex> p1.key) '''and''' (p2.key <tex> \le </tex> p3.key)
 
     minP = p2
 
     minP = p2
 
     p1 = p1
 
     p1 = p1
 
     p2 = p3
 
     p2 = p3
   '''if''' (p3.key <tex> \leqslant </tex> p1.key) '''and''' (p3.key <tex> \leqslant </tex> p2.key)
+
   '''if''' (p3.key <tex> \le </tex> p1.key) '''and''' (p3.key <tex> \le </tex> p2.key)
 
     minP = p3
 
     minP = p3
 
     p1 = p1
 
     p1 = p1
Строка 252: Строка 238:
 
Функция <tex>\mathrm{getKey(p)}</tex> по указателю p на элемент определяет значение его ключа:
 
Функция <tex>\mathrm{getKey(p)}</tex> по указателю p на элемент определяет значение его ключа:
 
  <font color=green>//под <tex>\infty</tex> нужно понимать нейтральный относительно минимума элемент.</font>
 
  <font color=green>//под <tex>\infty</tex> нужно понимать нейтральный относительно минимума элемент.</font>
  '''int''' getKey('''Node''' p):
+
  getKey(p):
 
   '''if''' p == NULL
 
   '''if''' p == NULL
 
     min = <tex>\infty</tex>
 
     min = <tex>\infty</tex>
Строка 262: Строка 248:
  
 
Функция <tex>\mathrm{minKeyNodeRoot(p)}</tex>, которая по указателю <tex>p</tex> на списочную часть разряда корневого счетчика возвращает указатель на корневой узел с минимальным ключом:
 
Функция <tex>\mathrm{minKeyNodeRoot(p)}</tex>, которая по указателю <tex>p</tex> на списочную часть разряда корневого счетчика возвращает указатель на корневой узел с минимальным ключом:
  '''Node''' minKeyNodeRoot('''Node''' p):
+
  minKeyNodeRoot(p):
 
   p1 = p
 
   p1 = p
 
   minP = p1
 
   minP = p1
Строка 273: Строка 259:
 
===Операция фиксации ===
 
===Операция фиксации ===
 
Операция фиксации <tex>\mathrm{rmFixRootCount(i)}</tex> для <tex>i</tex>-го разряда корневого счетчика подразумевает, что его значение равно трем, а списочная часть содержит указатель на список деревьев ранга  <tex>i</tex>, состоящий ровно из трех деревьев. При выполнении этой операции значение в  <tex>i</tex>-м разряде — должно стать равным нулю, а значение в  <tex>i</tex>-м разряде увеличиться на единицу. То есть в куче не должно остаться деревьев ранга  <tex>i</tex>, а количество деревьев ранга  <tex>i+1</tex> должно увеличиться на единицу. Для этого следует удалить из кучи три присутствующих в ней дерева ранга <tex>i</tex> , связать их в дерево ранга  <tex>i+1</tex> и вставить вновь полученное дерево в кучу.
 
Операция фиксации <tex>\mathrm{rmFixRootCount(i)}</tex> для <tex>i</tex>-го разряда корневого счетчика подразумевает, что его значение равно трем, а списочная часть содержит указатель на список деревьев ранга  <tex>i</tex>, состоящий ровно из трех деревьев. При выполнении этой операции значение в  <tex>i</tex>-м разряде — должно стать равным нулю, а значение в  <tex>i</tex>-м разряде увеличиться на единицу. То есть в куче не должно остаться деревьев ранга  <tex>i</tex>, а количество деревьев ранга  <tex>i+1</tex> должно увеличиться на единицу. Для этого следует удалить из кучи три присутствующих в ней дерева ранга <tex>i</tex> , связать их в дерево ранга  <tex>i+1</tex> и вставить вновь полученное дерево в кучу.
Следует учесть, что ранг нового дерева может стать больше, чем <tex>\mathtt{maxRank}</tex>, что потребует инициализации нового разряда. Для этого необходимо увеличить значение <tex>\mathtt{maxRank}</tex> на единицу и заполнить новое поле, а также провести инициализацию нового разряда.
+
Следует учесть, что ранг нового дерева может стать больше, чем <tex>maxRank</tex>, что потребует инициализации нового разряда. Для этого необходимо увеличить значение <tex>maxRank</tex> на единицу и заполнить новое поле, а также провести инициализацию нового разряда.
  '''void''' rmFixRootCount('''int''' i)
+
  rmFixRootCount(i)
 
   '''if''' maxRank == i
 
   '''if''' maxRank == i
 
     maxRank = i + 1
 
     maxRank = i + 1
Строка 292: Строка 278:
 
===Инкрементирование i-го разряда корневого счетчика===
 
===Инкрементирование i-го разряда корневого счетчика===
 
По сравнению с описанным алгоритмом инкрементирования <tex>i</tex>-го разряда избыточного представления здесь мы должны учесть работу со списочной частью и обновить прямые указатели.
 
По сравнению с описанным алгоритмом инкрементирования <tex>i</tex>-го разряда избыточного представления здесь мы должны учесть работу со списочной частью и обновить прямые указатели.
  '''void''' rmIncRootCount('''int''' i, '''Node''' p)
+
  rmIncRootCount(i,p)
 
   '''if''' (rootCount[i].Value == 1) '''or''' (rootCount[i].Value == 2)
 
   '''if''' (rootCount[i].Value == 1) '''or''' (rootCount[i].Value == 2)
 
     '''if''' rootCount[rootCount[i].forwardPointer].Value == 3
 
     '''if''' rootCount[rootCount[i].forwardPointer].Value == 3
Строка 306: Строка 292:
 
===Удаление дерева из кучи===
 
===Удаление дерева из кучи===
 
Процедура удаления дерева из кучи подразумевает наличие в куче этого дерева. Пусть удаляемое дерево имеет ранг <tex>i</tex> . Тогда значение <tex>i</tex>-го разряда избыточного корневого представления не равно нулю. То есть уменьшение этого значения на единицу не испортит регулярности представления и не потребует обновления каких-либо указателей. Необходимо лишь соответствующим образом обработать списочную часть.
 
Процедура удаления дерева из кучи подразумевает наличие в куче этого дерева. Пусть удаляемое дерево имеет ранг <tex>i</tex> . Тогда значение <tex>i</tex>-го разряда избыточного корневого представления не равно нулю. То есть уменьшение этого значения на единицу не испортит регулярности представления и не потребует обновления каких-либо указателей. Необходимо лишь соответствующим образом обработать списочную часть.
  '''void''' delete('''int''' i, '''Node''' p):
+
  delete(i, p):
 
   deleteTree(i, p)
 
   deleteTree(i, p)
 
   rootCount[i].Value = rootCount[i].Value - 1
 
   rootCount[i].Value = rootCount[i].Value - 1
  
 
===Нахождение дерева с минимальным ключом в корне <tex>\mathrm{minKey()}</tex>===
 
===Нахождение дерева с минимальным ключом в корне <tex>\mathrm{minKey()}</tex>===
  '''Node''' minKey()
+
  minKey()
 
   minP = NULL
 
   minP = NULL
 
   '''for''' i = 0 to maxRank:
 
   '''for''' i = 0 to maxRank:
Строка 329: Строка 315:
  
 
'''Счетчик нарушений''' представлен [[Саморасширяющийся массив|саморасширяющимся массивом]], элементы которого состоят из четырех полей:
 
'''Счетчик нарушений''' представлен [[Саморасширяющийся массив|саморасширяющимся массивом]], элементы которого состоят из четырех полей:
*<tex>\mathtt{countViolation[i].Value}</tex> {{---}} количество неправильных узлов ранга <tex>i</tex> в куче.
+
*<tex>countViolation[i].Value</tex> {{---}} количество неправильных узлов ранга <tex>i</tex> в куче.
*<tex>\mathtt{countViolation[i].forvardPointer}</tex> {{---}} прямой указатель <tex>i</tex>-го разряда
+
*<tex>countViolation[i].forvardPointer</tex> {{---}} прямой указатель <tex>i</tex>-го разряда
*<tex>\mathtt{countViolation[i].firstViolation}</tex> {{---}} указатель на неправильный узел ранга <tex>i</tex>
+
*<tex>countViolation[i].firstViolation</tex> {{---}} указатель на неправильный узел ранга <tex>i</tex>
*<tex>\mathtt{countViolation[i].secondViolation}</tex> {{---}} указатель на неправильный узел ранга <tex>i</tex>
+
*<tex>countViolation[i].secondViolation</tex> {{---}} указатель на неправильный узел ранга <tex>i</tex>
 +
 
  
 
{{Утверждение
 
{{Утверждение
Строка 346: Строка 333:
 
}}
 
}}
  
Для '''инициализации''' нового звена в счетчике нарушений необходимо лишь занулить его значение в новом разряде. Делается это только тогда, когда мы вводим в кучу новое дерево ранга <tex>\mathtt{maxRank + 1}</tex>. Это первый момент появления в куче узла ранга <tex>\mathtt{maxRank + 1}</tex>. Для тех нарушений, которые могут возникнуть в узлах ранга меньше либо равного <tex>\mathtt{maxRank + 1}</tex>, соответствующие разряды счетчика нарушений уже инициализированы, а узлов большего ранга в куче пока нет.
+
Для '''инициализации''' нового звена в счетчике нарушений необходимо лишь занулить его значение в новом разряде. Делается это только тогда, когда мы вводим в кучу новое дерево ранга <tex>maxRank + 1</tex>. Это первый момент появления в куче узла ранга <tex>maxRank + 1</tex>. Для тех нарушений, которые могут возникнуть в узлах ранга меньше либо равного <tex>maxRank + 1</tex>, соответствующие разряды счетчика нарушений уже инициализированы, а узлов большего ранга в куче пока нет.
  
 
==Основные операции==
 
==Основные операции==
Рассмотрим операции, которые можно производить с толстой кучей. Время работы основных операций указано в таблице:
+
* <tex>\mathrm{makeHeap}</tex> {{---}} <tex>O(1)</tex>
{| class="wikitable" style="width:10cm" border=1
+
Заключается в инициализации счетчиков.
|+
+
* <tex>\mathrm{findMin}</tex> {{---}} <tex>O(1)</tex>
|-align="center" bgcolor=#EEEEFF
+
Возвращает указатель на минимальный элемент.
! Операция || Время работы
+
* <tex>\mathrm{insert(key)}</tex> {{---}} <tex>O(1)</tex>
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{makeHeap}</tex>||<tex>O(1)</tex>
 
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{findMin}</tex>||<tex>O(1)</tex>
 
|-align="center" bgcolor=#FFFFFF
 
||<tex>\mathrm{insert(key)}</tex>||<tex>O(1)</tex>
 
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{decreaseKey}</tex>||<tex>O(1)</tex>
 
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{deleteMin}</tex>||<tex>O(\log(n))</tex>
 
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{delete}</tex>||<tex>O(\log(n))</tex>
 
|-align="center" bgcolor=#FFFFFF
 
|<tex>\mathrm{meld(h1, h2)}</tex>||<tex>O(\log(n))</tex>
 
|}
 
 
 
===makeHeap===
 
Заключается в инициализации счетчиков.
 
 
===findMin===
 
Возвращает указатель на минимальный элемент.
 
 
===insert(key)===
 
 
Чтобы выполнить эту операцию, делаем новый элемент отдельным деревом и выполняем процедуру вставки нового элемента ранга <tex>0</tex> в корневой счетчик. После этого, если необходимо, корректируем значение указателя на минимальный элемент.
 
Чтобы выполнить эту операцию, делаем новый элемент отдельным деревом и выполняем процедуру вставки нового элемента ранга <tex>0</tex> в корневой счетчик. После этого, если необходимо, корректируем значение указателя на минимальный элемент.
 
+
* <tex>\mathrm{decreaseKey}</tex> {{---}} <tex>O(1)</tex>
===decreaseKey===
 
 
Чтобы выполнить эту операцию, поступим следующим образом. Пусть <tex>x</tex>  — узел, на который указывает указатель <tex>p</tex> . Вычитаем <tex>\delta</tex>  из ключа узла <tex>x</tex> . Если новый ключ <tex>x</tex>  меньше минимального ключа кучи  <tex>H</tex>, обмениваем ключ элемента <tex>p</tex> с ключом минимального элемента. Новых нарушений операция не создаст. Пусть  <tex>r</tex> — ранг <tex>x</tex> . Если <tex>x</tex>  — нарушаемый узел, добавляем  <tex>x</tex> как новое <tex>r</tex>-ранговое нарушение инкрементированием <tex>r</tex>-й цифры <tex>d_r</tex>  счетчика нарушений.
 
Чтобы выполнить эту операцию, поступим следующим образом. Пусть <tex>x</tex>  — узел, на который указывает указатель <tex>p</tex> . Вычитаем <tex>\delta</tex>  из ключа узла <tex>x</tex> . Если новый ключ <tex>x</tex>  меньше минимального ключа кучи  <tex>H</tex>, обмениваем ключ элемента <tex>p</tex> с ключом минимального элемента. Новых нарушений операция не создаст. Пусть  <tex>r</tex> — ранг <tex>x</tex> . Если <tex>x</tex>  — нарушаемый узел, добавляем  <tex>x</tex> как новое <tex>r</tex>-ранговое нарушение инкрементированием <tex>r</tex>-й цифры <tex>d_r</tex>  счетчика нарушений.
 
+
* <tex>\mathrm{deleteMin}</tex> {{---}} <tex>O(\log(n))</tex>
===deleteMin===
 
 
Удаляем поддерево с корнем в минимальном узле из леса. Минимальность этого элемента гарантирует нам, что среди его детей нарушений порядка кучи не было. То есть нет необходимости работать со счетчиком нарушений. Затем вставляем в кучу все деревья с корнями, расположенными в детях удаляемого узла. Очевидно, что новый минимальный ключ — либо в корне дерева леса, либо в нарушенном узле. Выполняем поиск нового минимального элемента среди корней деревьев и нарушенных узлов.  
 
Удаляем поддерево с корнем в минимальном узле из леса. Минимальность этого элемента гарантирует нам, что среди его детей нарушений порядка кучи не было. То есть нет необходимости работать со счетчиком нарушений. Затем вставляем в кучу все деревья с корнями, расположенными в детях удаляемого узла. Очевидно, что новый минимальный ключ — либо в корне дерева леса, либо в нарушенном узле. Выполняем поиск нового минимального элемента среди корней деревьев и нарушенных узлов.  
 
Если минимальный элемент оказался в нарушенном узле, то обмениваем его с элементом, хранимым в корне этого дерева, корректируя корневой счетчик, если это необходимо. После замены новый минимум — в корне дерева леса. Этот корень будет новым минимальным узлом.
 
Если минимальный элемент оказался в нарушенном узле, то обмениваем его с элементом, хранимым в корне этого дерева, корректируя корневой счетчик, если это необходимо. После замены новый минимум — в корне дерева леса. Этот корень будет новым минимальным узлом.
 
+
* <tex>\mathrm{delete}</tex> {{---}} <tex>O(\log(n))</tex>
===delete===
 
 
Выполняем <tex>\mathrm{decreaseKey}</tex> а затем <tex>\mathrm{deleteMin}</tex>.
 
Выполняем <tex>\mathrm{decreaseKey}</tex> а затем <tex>\mathrm{deleteMin}</tex>.
 
+
* <tex>\mathrm{meld(h1, h2)}</tex> {{---}} <tex>O(\log(n))</tex>
===meld(h1, h2)===
+
Первый шаг — фиксируются все нарушения в куче с меньшим максимальным рангом (разрывая связь произвольно). Не уменьшая общности, считаем, что эта куча — <tex>р2</tex> . Пройти по счетчику нарушений <tex>p2</tex>  от младшей цифры к старшей, пропуская цифры со значением <tex>0</tex> . Для  <tex>i</tex>-й цифры  <tex>d_i != 0</tex> делаем операцию фиксирования на каждой цифре, показываемой прямым указателем <tex>d_i</tex> , если эта цифра имеет значение 2. Затем, если <tex>d_i = 2</tex> , фиксируем <tex>d_i</tex> . Если <tex>d_i = 1</tex> , преобразуем это  <tex>i</tex>-ранговое нарушение в  <tex>(i+1)</tex>-ранговое нарушение, как при фиксировании, используя  <tex>i</tex>-рангового брата нарушенного узла вместо (несуществующего) другого  <tex>i</tex> -рангового нарушения.
Первый шаг — фиксируются все нарушения в куче с меньшим максимальным рангом (разрывая связь произвольно). Не уменьшая общности, считаем, что эта куча — <tex>р2</tex> . Пройти по счетчику нарушений <tex>p2</tex>  от младшей цифры к старшей, пропуская цифры со значением <tex>0</tex> . Для  <tex>i</tex>-й цифры  <tex>d_i != 0</tex> делаем операцию фиксирования на каждой цифре, показываемой прямым указателем <tex>d_i</tex> , если эта цифра имеет значение <tex>2</tex>. Затем, если <tex>d_i = 2</tex> , фиксируем <tex>d_i</tex> . Если <tex>d_i = 1</tex> , преобразуем это  <tex>i</tex>-ранговое нарушение в  <tex>(i+1)</tex>-ранговое нарушение, как при фиксировании, используя  <tex>i</tex>-рангового брата нарушенного узла вместо (несуществующего) другого  <tex>i</tex> -рангового нарушения.
+
Как только <tex>h2</tex>  не будет содержать каких-либо нарушений, нужно вставить корни из корневого счетчика <tex>h2</tex>  в корневой счетчик <tex>h1</tex> инкрементированием соответствующих цифр. Если минимальный узел  <tex>h2</tex> содержит меньший ключ, чем минимальный узел <tex>h1</tex> , следует установить новым минимальным узлом  <tex>h1</tex> минимальный узел <tex>h2</tex> . Затем нужно вернуть модифицированную кучу <tex>h1</tex>  в качестве результата <tex>\mathrm{meld}</tex> .  
Как только <tex>h2</tex>  не будет содержать каких-либо нарушений, нужно вставить корни из корневого счетчика <tex>h2</tex>  в корневой счетчик <tex>h1</tex> инкрементированием соответствующих цифр. Если минимальный узел  <tex>h2</tex> содержит меньший ключ, чем минимальный узел <tex>h1</tex> , следует установить новым минимальным узлом  <tex>h1</tex> минимальный узел <tex>h2</tex> . Затем нужно вернуть модифицированную кучу <tex>h1</tex>  в качестве результата <tex>\mathrm{meld}</tex> .
+
* <tex>\mathrm{deleteViolation}</tex>
 
===deleteViolation===
 
 
Для освобождения кучи от нарушений достаточно выполнить следующий псевдокод:
 
Для освобождения кучи от нарушений достаточно выполнить следующий псевдокод:
  '''void''' deleteViolation('''Node''' h2):
+
  deleteViolation(h2):
 
   '''for''' i = 0 '''to''' h2.maxRank
 
   '''for''' i = 0 '''to''' h2.maxRank
 
     '''if''' countViolation[i].Value == 2
 
     '''if''' countViolation[i].Value == 2
Строка 404: Строка 363:
 
       fixCountViolation(i)
 
       fixCountViolation(i)
  
==См. также==
+
==Источники==
* [[Тонкая куча]]
 
 
 
==Источники информации==
 
 
* [http://www.intuit.ru/studies/courses/100/100/lecture/2935?page=1 INTUIT.ru {{---}} Толстые кучи]
 
* [http://www.intuit.ru/studies/courses/100/100/lecture/2935?page=1 INTUIT.ru {{---}} Толстые кучи]
 
* [https://www.lektorium.tv/lecture/14234  CS center {{---}} Приоритетные очереди]
 
* [https://www.lektorium.tv/lecture/14234  CS center {{---}} Приоритетные очереди]
* [http://www.cs.tau.ac.il/~haimk/papers/newthin1.pdf H.Kaplan, R.Tarjan. "Thin Heaps, Thick Heaps".  2006]
 
  
 
[[Категория: Дискретная математика и алгоритмы]]
 
[[Категория: Дискретная математика и алгоритмы]]
 
[[Категория: Приоритетные очереди]]
 
[[Категория: Приоритетные очереди]]
[[Категория: Структуры данных]]
 

Пожалуйста, учтите, что любой ваш вклад в проект «Викиконспекты» может быть отредактирован или удалён другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см. Викиконспекты:Авторские права). НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!

Чтобы изменить эту страницу, пожалуйста, ответьте на приведённый ниже вопрос (подробнее):

Отменить | Справка по редактированию (в новом окне)