
Дональд Кнут. Искусство программирования. т.3 / knuth3
.pdfOriginal pages: 483-535 281
Если же он меньше, поместите по адресу 2n−2. : : : ”
Другая ранняя форма вставки в дерево была введена Д. Дж. Уилером; он, в сущности, допускал многопутевые разветвления, подобные тем, которые будут обсуждаться в п. 6.2.4; метод вставки в бинарное дерево был также независимо придуман К. М. Бернесом-Ли [Comp. J., 2 (1959), 5].
Первые опубликованные описания вставки в дерево принадлежат П. Ф. Уиндли [Comp. J., 3 (1960), 84–88], Э. Д. Буту и Э. Колину [Information and Control, 3 (1960), 327–334] и Т. Н. Хиббарду [JACM, 9 (1962), 13–28]. Вероятно, все авторы развивали этот метод независимо, и их доказательства формулы (6) среднего числа сравнений несколько отличаются друг от друга. В последующих работах они продолжали исследовать различные аспекты этого алгоритма: Уиндли подробно разобрал сортировку вставкой в дерево, Бут и Колин обсуждали эффект предварительного построения из первых 2n −1 элементов идеально сбалансированного дерева (см. упр. 4), Хиббард предложил идею удаления и выявил связь между анализом вставки в дерево и анализом быстрой сортировки.
Идея оптимальных бинарных деревьев поиска была впервые развита для частного случая p1 =
= pn = 0 в связи с бинарным кодированием букв. В очень интересной статье Э. Гилберта и Э. Мура [Bell System Tech. J., 38 (1959), 933–968] обсуждалась эта проблема и ее связь с другими задачами кодирования. Гилберт и Мур отметили, что оптимальное дерево можно построить за O(n3) шагов, используя метод, подобный алгоритму K, но без соотношения (17). К. Э. Айверсон [A Programming Language (Wiley, 1962), 142–144] независимо рассмотрел другой случай, когда все q равны 0. Он предположил, что дерево будет оптимальным, если корень выбрать так, чтобы как можно лучше уравнять вероятности левого и правого поддеревьев; мы видели, что, к сожалению, эта идея не работает. Д. Э. Кнут [Acta Informatica, 1 (1971), 14–25, 270] впоследствии рассмотрел случай любых весов p и q и доказал, что алгоритм может быть сведен к O(n2) шагам; он также привел пример применения рассмотренных методов к компилятору, где ключами в дереве являются ”резервированные слова” алголоподобного языка. Т. Ч. Ху несколько лет занимался своим собственным алгоритмом для случая p = 0; из-за сложности задачи было трудно найти строгое доказательство справедливости этого алгоритма, но в конце концов в 1969 г. Т. Ч. Ху и Э. К. Такер справились с этим. [SIAM J. Applied Math., 21 (1971), 514–532.]
Упражнения
1.[15] Алгоритм T был сформулирован лишь для непустых деревьев. Какие нужно внести изменения, чтобы он обрабатывал и пустые деревья?
2.[20] Измените алгоритм T так, чтобы он работал с правопрошитыми деревьями (ср. с п. 2.3.1; по таким деревьям проще совершать симметричный обход).
>3. [20] В x6.1 мы видели, что небольшое изменение алгоритма последовательного поиска 6.1S делает его быстрее (алгоритм 6.1Q). Можно ли подобную хитрость использовать для ускорения работы алгоритма T?
4.[М24] (Э. Д. Бут и Э. Колин.) Даны N ключей в случайном порядке; предположим, что мы используем первые 2n − 1 для построения идеально сбалансированного дерева, помещая 2k ключей на уровень k, 0 k < n. Затем мы применяем алгоритм T для вставки оставшихся ключей. Найдите
среднее число сравнений при удачном поиске. [Указание: видоизменить (2).]
>5. [М25] Названия CAPRICORN, AQUARIUS и т. д. можно упорядочить 11! = 39916800 способами для вставки в бинарное дерево поиска. (a) Сколько таких перестановок приведут к рис. 10? (b) Сколько перестановок приведут к вырожденному дереву, т. е. во всех узлах LLINK или RLINK будут равняться ?
6.[М26] Обозначим через Pnk количество перестановок a1 a2 : : :an множества f1; 2; : : : ; n g, таких, что, если алгоритм T используется для последовательной вставки a1, a2, : : : , an в первоначально
пустое дерево, ровно k сравнений производится при вставке an. [В этой задаче мы пренебрежем
сравнениями, производимыми при вставкеa1, : : : , an−1. В принятых в тексте обозначениях Cn0 −1 = |
|||
P |
|
− 1 |
|
( kPnk)=n!, так как это есть среднее число сравнений, которые производятся при неудачном |
|||
поиске по дереву из n |
|
элементов.] |
|
a) |
Докажите, что Pn+1;k = 2Pn;k−1 + (n − 1)Pn;k. [Указание: посмотрите, оказывается ли |
||
|
an+1 ниже an.] |
|
|
b) |
Найдите простую формулу для производящей функции Gn(z) = Pk Pnkzk и используйте |
еедля выражения Pnk через числа Стирлинга.
c)Чему равна дисперсия Cn0 −1?
7.[M30] (С. Р. Арора и У. Т. Дэнт.) Какое среднее число сравнении потребуется для отыскания m-го по величине элемента после вставки в первоначально пустое дерево n элементов в случайном порядке?

Original pages: 483-535 283
введенному в упр. 25 (веса (p1, : : :, pn; q0, : : : , qn) предполагаются неотрицательными). Доказательство проводится индукцией по j − i; наша задача—доказать, что R(0; n − 1) R(0; n) при n 2, в предположении справедливости соотношения при j − i < n. [В силу симметрии отсюда следует, что R(0; n) R(1; n).]
a)Докажите, что R(0; n − 1) R(0; n), если pn = qn = 0. (См. упр. 24.)
b)Пусть pn+qn = x. Через Rh обозначим множество R(0; n)оптимальных корней, если xh < x < xh+1,
ачерез Rh0 —множество оптимальных корней при xh = x (см. обозначения упр. 26). Докажите,
что
R00 R0 R10 R1 : : : Rm0 Rm:
Отсюда, из части (a) и упр. 25 следует, что R(0; n − 1) R(0; n) для всех x.
[Указание. Рассмотрите случай x = xh и предположите, что оба эти дерева
Picture: |
Рис. стр. 534 |
оптимальны; s < r и l l0. Используйте предположение индукции для доказательства того, что существует оптимальное дерево с корнем (r) и узлом n на уровне l0, а также оптимальное дерево с корнем (s) и узлом n на уровне l.]
28.[24] Используйте некоторый макроязык для написания макроопределения ”оптимальный бинарный поиск”, параметром которого является вложенная спецификация оптимального бинарного дерева.
29.[40] Каково наихудшее возможное бинарное дерево поиска для 31 наиболее употребительного английского слова? (Частоты слов даны на рис. 12.)
30.[M41] Докажите или опровергните, что цена оптимальных бинарных деревьев поиска удовлетворяет соотношению c(i; j) + c(i + 1; j − 1) c(i; j − 1) + c(i + 1; j).
31.[М20] (a) Если веса (q0, : : :, q5) в (22) равны (2, 3, 1, 1, 3, 2) соответственно, то какова взвешенная длина пути дерева? (b) Чему равна взвешенная длина пути оптимального бинарного дерева
поиска, имеющего ту же последовательность весов?
>32. [М22] (T. Ч. Ху и Э. К. Такер.) Докажите, что веса qi + qj новых узлов, получаемые на фазе 1 алгоритма Ху-Такера, создаются в неубывающем порядке.
33.[М41] Чтобы найти бинарное дерево поиска, минимизирующее время работы программы T, нужно минимизировать не просто число сравнений C, а величину 7C +C1. Придумайте алгоритм для нахождения оптимальных бинарных деревьев поиска, если левые и правые ветви дерева имеют различные цены. [Кстати, когда цена правой ветви в два раза больше цены левой ветви, а частоты всех узлов равны, оптимальными оказываются фибоначчиевы деревья. Ср. с L. Е. Stanfel, JACM, 17 (1970), 508–517.]
34.[41] Напишите программу для алгоритма Ху-Такера, требующую O(n) единиц памяти и O(n log n) единиц времени.
35.[М23] Покажите, что коды, построенные в доказательстве теоремы G, обладают тем свойством, что Ci никогда не начинается с Cj при i 6= j.
36.[М40] (П. Дж. Бэйер.) Обобщив верхнюю оценку в теореме G, докажите, что цена любого оп-
тимального бинарного дерева поиска с неотрицательными весами не превосходит полного ве-
P P
са S = 1lei n pi + 0 i n qi, умноженного на H + 2, где
XX
H = |
(pi=S)log2(S=pi) + |
(qi=S)log2(S=qi); |
1 i n |
|
0 i n |
на самом деле процедура выбора корней, минимизирующая максимальный вес поддерева, позволяет построить дерево бинарного поиска, удовлетворяющее этой оценке. Покажите далее, что
цена оптимального бинарного дерева поиска не превышает S, умноженного на H |
|
log |
2(2 |
H=e . |
|
|
−m |
|
) |
||
>37. [М25] (Т. Ч. Ху и К. Ч. Тань.) Пусть n + 1 = 2m + k, где 0 k 2m. Существует |
2k |
бинарных |
деревьев, в которых все внешние узлы расположены на уровнях m и m + 1. Покажите, что среди всех этих деревьев мы найдем одно с минимальной взвешенной длиной пути для последовательности весов (q0, : : : , qn), если применим алгоритм Ху-Такера к последовательности (M + q0, : : : ,
M + qn) для достаточно большого M.
38.[М35] (К. Ч. Тань.) Докажите, что среди всех множеств вероятностей (p1, : : : , pn; q0, : : :, qn), p1 + + pn +q0 + + qn = 1 дерево с минимальной ценой наиболее дорого, если pi = 0 для всех i, qj = 0 для четных j и qj = 1=bn=2c для нечетных j. [Указание. Для произвольных вероятностей (p1, : : :, pn; q0, : : :, qn) положим c0 = q0, ci = pi + qi при 1 i n и S(0) = ;, а для 1 r dn=2e

Original pages: 536-544 285
Бинарное дерево называется сбалансированным; если высота левого поддерева каждого узла отличается от высоты правого поддерева не более чем на 1. На рис. 20 показано сбалансированное дерево с 17 внутренними узлами и высотой 5; показатель сбалансированности представлен внутри каждого узла знаками +, или −, что отвечает разности высот правого и левого поддеревьев, равной +1, 0 или −1 соответственно. Фибоначчиево дерево на рис. 8 (п 6.2.1) является другим сбалансированным бинарным деревом высоты 5, имеющим только 12 внутренних узлов; большинство показателей сбалансированности равно −1. ”Зодиакальное дерево” на рис. 10 (п. 6.2.2) не сбалансировано, так как поддеревья узлов AQUARIUS и GEMINI не удовлетворяют принятым ограничениям.
Это определение сбалансированности представляет собой компромисс между оптимальными бинарными деревьями (все внешние узлы которых расположены на двух смежных уровнях) и произвольными бинарными деревьями. Поэтому уместно спросить, как далеко может отклониться от оптимальности сбалансированное дерево? Оказывается, что длина его поискового пути .никогда не превысит оптимум более чем на 45%.
Теорема А. (Г. М. Адельсон-Вельский и Е. М. Ландис). Высота сбалансированного дерева с N внутренними узлами заключена между log2(N + 1) и 1:4404 log2(N + 2) − 0:328.
Доказательство. Бинарное дерево высоты h, очевидно, не может содержать более чем 2h внешних узлов; поэтому N + 1 2h, т.е. h dlog2(N + 1)e.
Чтобы найти максимальное значение h, поставим вопрос по-другому: каково минимальное число узлов в сбалансированном дереве высоты h? Пусть Th— такое дерево с наименьшим возможным количеством узлов; тогда одно поддерево корня, например левое, имеет высоту h − 1, а другое—или h−1, или h−2. В силу определения Th можно считать, что левое поддерево корня есть Th−1, а правое— Th−2. Таким образом, среди всех сбалансированных деревьев высоты h наименьшее количество узлов имеет фибоначчиево дерево порядка h + 1. (См. определение деревьев Фибоначчи в п. 6.2.1.) Итак,
h+2 |
p |
|
|
|
|||
N Fh+2 − 1 > |
= 5 − 2; |
и требуемый результат получается так же, как следствие из теоремы 4.5.3F.
Мы видим, что поиск в сбалансированном дереве потребует более 25 сравнений, только если дерево состоит из по крайней мере F27 − 1 = 196417 узлов.
Рассмотрим теперь, что происходит, когда новый узел вставляется в сбалансированное дерево посредством алгоритма 6.2.2Т. Дерево на рис. 20 остается сбалансированным, если новый узел займет место одного из узлов 4 , 5 , б , 7 , 10 или 13 , но в других случаях потребуется некоторая корректировка. Трудности возникают, если имеется узел с показателем сбалансированности +1, правое поддерево которого после вставки становится выше, или если показатель сбалансированности равен −1 и выше. становится левое поддерево. Легко понять, что, в сущности, нас беспокоят лишь два случая:
Picture: |
Случаи вставки в АВЛ-дерево |
(Другие ”плохие” случаи можно получить, зеркально отразив эти диаграммы относительно вертикальной оси.) Большими прямоугольниками , , γ, обозначены поддеревья с соответствующими высотами. Случай 1 имеет место, если новый элемент увеличил высоту правого поддерева узла B с h до h + 1, а случай 2—когда новый элемент увеличивает высоту левого поддерева узла B. Во втором случае мы имеем либо h = 0 (и тогда сам узел X является новым узлом), либо узел X имеет два поддерева с соответственными высотами (h − 1; h) или (h; h − −l):
Простые преобразования восстанавливают баланс в обоих случаях, сохраняя в то же время симметричный порядок узлов A, B и X.
Picture: Повороты
В случае 1 мы просто поворачиваем дерево налево, прикрепляя к A вместо B. Это преобразование подобно применению ассоциативного закона к алгебраической формуле, когда мы заменяем ( γ) на ( )γ. В случае 2 это проделывается дважды: сначала (X; B) поворачивается направо, затем (A; X)— налево. В обоих случаях нужно изменить в дереве лишь несколько ссылок. Далее новые деревья имеют высоту h+2, в точности ту же, что и до вставки элемента; следовательно, часть дерева, расположенная над узлом A (если таковая имеется), остается сбалансированной.
Picture: |
Дерево рис. 20, сбалансированное после вставки нового ключа R |

286 Original pages: 536-544
Например, если мы вставляем новый узел на место 17 (рис. 20), то после поворота получим сбалансированное дерево, изображенное на рис. 21 (случай 1). Заметьте, что некоторые из показателей сбалансированности изменились.
Детали этой процедуры вставки можно разработать различными способами. На первый взгляд без вспомогательного стека не обойтись, так как необходимо запоминать узлы, которые будут затронуты вставкой. Ниже приводится алгоритм, в котором, прибегнув к маленькой хитрости, мы обходимся без стека, выигрывая при этом в скорости.
Алгоритм А. (Поиск, с вставкой по сбалансированному дереву.) Имеется таблица записей, образующих сбалансированное бинарное дерево. Алгоритм позволяет произвести поиск данного аргумента K. Если K в таблице нет, в подходящем месте в дерево вставляется новый узел, содержащий K. При необходимости производится балансировка дерева.
Предполагается (как и в алгоритме 6.2.2Т), что узлы содержат поля KEY, LLINK и RLINK. Кроме того, имеется новое поле B(P) = показатель сбалансированности узла NODE(P), т. е. разность высот правого и левого поддеревьев; это поле всегда содержит +1, 0 или −1. По адресу HEAD расположен специальный головной узел; RLINK (HEAD) указывает на корень дерева, a LLINK (HEAD) используется для хранения полной высоты дерева. Для данного алгоритма высота не имеет значения, но знание ее полезно для процедуры конкатенации, обсуждающейся ниже. Мы предполагаем, что дерево непусто,
т.е. что RLINK (HEAD)6= .
Вцелях удобства описания в алгоритме используется обозначение LINK (а, P) как синоним LLINK (P) при a = −1 и как синоним RLINK (P) при a = +1.
А1 [Начальная установка.] Установить T HEAD, S P RLINK(HEAD). [Указательная переменная P будет двигаться вниз по дереву; S будет указывать на место, где может потребоваться балансировка; T всегда указывает на отца S.]
А2 [Сравнение.] Если K < KEY(P), то перейти на А3; если K > KEY(P), то перейти на А4; если K = KEY(P), поиск удачно завершен.
А3 [Шаг влево.] Установить Q LLINK(P). Если Q = , выполнить Q ( AVAIL и LLINK(P) Q; затем идти на А5. В противном случае, если B(Q) 6= 0, установить T P и S Q. Наконец, установить P Q и вернуться на А2.
А4 [Шаг вправо.] Установить Q RLINK(P). Если Q = , выполнить Q ( AVAIL и RLINK(P) Q; затем
идти на А5. |
|
|
В противном случае, если B(Q) 6= 0, установить T |
P и S |
Q. Наконец, установить P Q и |
вернуться на А2. (Последнюю часть этого шага можно объединить с последней частью шага А3.)
А5 [Вставка.] (Мы только что присоединили новый узел NODE (Q) к дереву; теперь его поля нуждаются в начальной установке.) Установить KEY(Q) K, LLINK(Q) RLINK(Q) , B(Q) 0.
А6 [Корректировка показателей сбалансированности.] (Теперь нулевые показатели сбалансирован-
ности между S и Q нужно заменить на 1.) Если K < KEY(S), установить R |
P |
LLINK(S); в |
|
противном случае установить R P |
RLINK(S). Затем нужно 0 или более раз повторять следую- |
||
щую операцию, пока P не станет равным Q: если K < KEY(P), установить B(P) |
−1 и P |
LLINK(P); |
|
если K > KEY(P), установить B(P) |
+1 и P RLINK(P). (Если K = KEY(P), значит, P = Q, и можно |
перейти к следующему шагу.)
А7 [Проверка сбалансированности.] ЕслиK < KEY(S), установить a −1; в противном случае a +1. Теперь возможны три случая:
i) Если B(S) = 0 (дерево стало выше), установить B(S) a, LLINK(HEAD) LLINK(HEAD) + 1; алгоритм завершен.
ii Если B(S) = −a (дерево стало более сбалансированным), установить B(S) 0; алгоритм завершен.
iii)Если B(S) = a (дерево перестало быть сбалансированным), при B(R) = a идти на А8, при B(R) = −a идти на А9.
(Случай (iii) соответствует ситуации, изображенной на диаграмме (1), при a = +1; S и R указывают соответственно на узлы A и B, a LINK(−a; S) указывает на и т.д.)
А8 [Однократный поворот.] Установить P |
R, LINK(a; S) |
LINK(−a; R), LINK(−a; R) |
S, B(S) |
|
|||||
B(R) 0. Перейти на А10. |
LINK(−a; R), LINK(−a; R) |
|
|
|
|||||
А9 [Двукратный поворот.] Установить P |
LINK(a; P), LINK(a; P) |
R, |
|||||||
LINK(a; S) |
LINK(−a; P), LINK(−a; P) S. Теперь установить |
|
|
|
|
|
|||
|
|
8 |
(−a; 0); |
если B(P) = a; |
|
|
|||
|
(B(S); B(R)) |
(0; 0); |
если B(P) = 0; |
|
(3) |
||||
|
|
< |
(0; a); |
если B(P) = |
|
a; |
|
|
|
затем B(P) |
0. |
: |
|
|
|
− |
|
|
|
Original pages: 536-544 287
А10 [Последний штрих.] [Мы завершили балансирующее преобразование от (1) к (2), P указывает на
новый корень, а T—на отца старого корня.] Если S = RLINK(T), то установить RLINK(T) |
P; в |
||
противном случае LLINK(T) P. |
|
|
|
Этот алгоритм довольно длинный, но разделяется на три простые части: шаги А1–А4 (поиск), шаги А5–А7 (вставка нового узла), шаги А8–А10 (балансировка дерева, если она нужна).
Picture: 22. Поиск с вставкой по сбалансированному дереву
Мы знаем, что для работы алгоритма требуется около C log N единиц времени при некотором C, но чтобы знать, при каких N выгодно использовать сбалансированные деревья, нужно оценить величину C. Анализ следующей MIX-программы позволяет подойти к решению этого вопроса.
Программа А. (Поиск с вставкой по сбалансированному дереву.) Эта реализация алгоритма А использует следующий формат узлов дерева:
Picture: Формат узла АВЛ-дерева
rA K, rI1 P, rI2 Q, rI3 R, rI4 S, rI5 T. Программа для шагов А7–А9 дублируется, так что величина a в явном виде в программе не фигурирует.
B |
EQU |
0:1 |
|
|
|
|
|
LLINK |
EQU |
2:3 |
|
|
|
|
|
RLINK |
EQU |
4:5 |
|
|
|
|
|
START |
LDA |
K |
|
1 |
A1. Начальная установка. |
||
|
ENT5 |
HEAD |
|
1 |
T |
HEAD. |
|
|
LD2 |
0,5 |
(RLINK) |
1 |
Q RLINK(HEAD). |
|
|
|
JMP |
2F |
|
1 |
На А2 с S P Q |
|
|
4Н |
LD2 |
0,1 |
(RLINK) |
C2 |
А4. Шаг вправо. Q |
RLINK(P) |
|
|
J2Z |
5F |
|
C2 |
На А5, если Q = . |
|
|
1Н |
LDX |
0,2 |
(B) |
C − 1 |
rX B(Q). |
|
|
|
JXZ |
*+3 |
|
C − 1 |
Переход, если B(Q) = 0. |
||
|
ENT5 |
0,1 |
|
D − 1 |
T |
P. |
|
2H |
ENT4 |
0,2 |
|
D |
S |
Q. |
|
|
ENT1 |
0,2 |
|
C |
P |
Q. |
|
|
CMPA |
1,1 |
|
C |
А2. Сравнение. |
|
|
|
JG |
4B |
|
C |
На А4,если K > KEY(P). |
||
|
JE |
SUCCESS |
C1 |
Выход, если K = KEY(P). |
|||
|
LD2 |
0,1 |
(LLINK) |
C1 − S |
A3. Шаг влево. Q |
LLINK(P). |
|
|
J2NZ |
1B |
|
C1 − S |
Переход, если Q 6= . |
||
20–29 5Н (скопировать здесь строки 14—23 программы 6.2.2 Т) А5. Вставка. |
|||||||
6H |
CMPA |
1,4 |
|
1 − S |
A6. Коррект. показат. сбалансир. |
||
|
JL |
*+3 |
|
1 − S |
Переход, если K < KEY(S). |
||
|
LDS |
0,4 |
(RLINK) |
E |
R |
RLINK(S). |
|
|
JMP |
*+2 |
|
E |
|
|
|
|
LD3 |
0,4 |
(LLINK) |
1 − S − E |
R |
LLINK(S). |
|
|
ENT1 |
0,3 |
|
1 − S |
P |
R. |
|
|
ENTX |
-1 |
|
1 − S |
rX |
−1. |
|
|
JMP |
1F |
|
1 − S |
На цикл сравнения. |
||
4Н |
JE |
7F |
|
F2 + 1 − S |
На А7, если K = KEY(P). |
||
|
STX |
0,1 |
(1:1) |
F2 |
B(P) +1 (он был +0). |
||
|
LD1 |
0,1 |
(RLINK) |
F2 |
P |
RLINK(P). |
|
1Н |
CMPA |
1,1 |
|
F + 1 − S |
Переход, если K KEY(P). |
||
|
JGE |
4B |
|
F + 1 − S |
|||
|
STX |
0,1 |
(B) |
F1 |
B(P) |
−1. |
|
|
LD1 |
0,1 |
(LLINK) |
F1 |
P LLINK(P). |
|
|
|
JMP |
1B |
|
F1 |
На цикл сравнения. |
||
7Н |
LD2 |
0,4(B) |
1 − S |
A7. Проверка сбалансир. rI2 B(S). |
|||
|
STZ |
0,4 |
(B) |
1 − S |
B(S) |
0. |
|
288 Original pages: 545-571 |
|
|
|
|
|
|
|
|||||
|
|
|
CMPA |
1,4 |
|
1 − S |
На a = +1 подпрограмму, если K > KEY(S). |
|||||
|
|
|
JG |
|
A7R |
|
1 − S |
|||||
|
A7L |
J2P |
DONE |
A7R |
J2N |
DONE |
1 − S |
Выход, если rl2 = −a. |
||||
|
|
|
J2Z |
7F |
|
J2Z |
6F |
G + J |
Переход, если B(S) был нулем. |
|||
|
|
|
ENT1 |
0,3 |
|
ENT1 |
0,3 |
G |
P |
R. |
|
|
|
|
|
LD2 |
0,3(B) |
|
LD2 |
0,3(B) |
G |
rI2 B(R). |
|
||
|
|
|
J2N |
A8L |
|
J2P |
A8R |
G |
На. A8, если rI2 = a. |
|||
|
A9L |
LD1 |
0,3(RLINK) |
A9R |
LD1 |
0,3(LLINK) |
H |
A9. Двукратный поворот. |
||||
|
|
|
LDX |
0,1(LLINK) |
|
LDX |
0,1(RLINK) |
H |
LINK(a; P LINK(−a; R)) |
|||
|
|
|
STX |
0,3(RLINK) |
|
STX |
0,3(LLINK) |
H |
! LINK(−a; R). |
|||
|
|
|
ST3 |
0,1(LLINK) |
|
ST3 |
0,1(RLINK) |
H |
LINK(a; P) |
R. |
||
|
|
|
LD2 |
0,1(B) |
|
LD2 |
0,1(B) |
H |
rI2 |
B(P). |
|
|
|
|
|
LDX |
T1,2 |
|
LDX |
T2,2 |
H |
−a, 0 или 0 |
|
||
|
|
|
STX |
0,1(B) |
|
STX |
0,4(B) |
H |
! B(S). |
|
||
|
|
|
LDX |
T2,2 |
|
LDX |
T1,2 |
H |
0, 0 или a |
|
||
|
|
|
STX |
0,3(B) |
|
STX |
0,3(B) |
H |
! B(R) |
|
||
|
A8L |
LDX |
0,1(RLINK) |
A8R |
LDX |
0,1(LLINK) |
G |
A8. Однократный поворот. |
||||
|
|
|
STX |
0,4(LLINK) |
|
STX |
0,4(RLINK) |
G |
LINK(a; S) |
LINK(−a; P). |
||
|
|
|
ST4 |
0,1(RLINK) |
|
ST4 |
0,1(LLINK) |
G |
LINK(−a; P) |
S. |
||
|
|
|
JMP |
A8R1 |
A8R1 |
STZ |
0,1(B) |
G |
B(P) 0. |
|
||
|
A10 |
CMP4 |
0,5(RLINK) |
G |
A10. Последний штрих. |
|
|
|||||
|
|
|
JNE |
|
*+3 |
|
G |
Переход, если RLINK(T) 6= S. |
|
|||
|
|
|
ST1 |
|
0,5(RLINK) |
G2 |
RLINK(T) P. |
|
|
|
|
|
|
|
|
JMP |
|
DONE |
|
G2 |
Выход. |
|
|
|
|
|
|
|
ST1 |
|
0,5(LLINK) |
G1 |
LLINK(T) P. |
|
|
|
|
|
|
|
|
JMP |
|
DONE |
|
G1 |
Выход. |
|
|
|
|
|
|
|
CON |
|
+1 |
|
|
|
|
|
|
|
|
T1 |
CON |
|
0 |
|
|
Таблица для (3). |
|
|
|
||
|
T2 |
CON |
|
0 |
|
|
|
|
|
|
|
|
|
|
|
CON |
|
-1 |
|
|
|
|
|
|
|
|
6H |
ENTX |
+1 |
|
J2 |
rX +1. |
|
|
|
|
||
|
7H |
STX |
|
0,4(B) |
|
J |
B(S) a. |
|
|
|
|
|
|
|
|
LDX |
|
HEAD(LLINK) |
J |
LLINK(HEAD). |
|
|
|
|
|
|
|
|
INCX |
1 |
|
J |
+1 |
|
|
|
|
|
|
|
|
STX |
|
HEAD(LLINK) |
J |
! LLINK(HEAD). |
|
|
|
||
|
DONE |
EQU |
|
* |
|
1 − S |
Вставка завершена. |
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
Анализ вставки в сбалансированное дерево. [Читатели, не интересующиеся математикой, могут сразу перейти к формуле (10).] Чтобы вычислить время работы алгоритма A, нужно сначала ответить на следующие вопросы:
Сколько сравнений производится во время поиска?
Как далеко друг от друга будут находиться узлы S и Q? (Иными словами, сколько нужно произвести корректировок в шаге A6?)
Как часто нужно производить однократный или двукратный поворот?
Воспользовавшись теоремой A, нетрудно вывести верхнюю оценку времени работы, но нас, разумеется, интересует средний уровень. До сих пор не удалось теоретически оценить, как ведет себя алгоритм в среднем, поскольку он оказался довольно сложным, однако были получены некоторые интересные эмпирические результаты.
В первую очередь нас может интересовать число Bnh сбалансированных бинарных деревьев с n внутренними узлами и высотой h. Для небольших h из соотношений
|
B0(z) = 1; |
B1(z) = z; Bh+1(z) = zBh(z)(Bh(z) + 2Bh−1(z)) |
(5) |
||||||
|
вычислить производящую функцию B |
z |
|
B |
|
zn |
|
|
|
нетрудно |
B2(z) = 2z2 + z3; |
|
h( ) = Pn 0 |
|
nh |
|
(см. упр. 6). Таким образом, |
|
|
|
B3(z) = |
4z4 + 6z5 + 4z6 +z7 |
+32z8 + 44z9 + + 8z14 + z15; |
|
|||||
|
B4(z) = |
|
16z7 |
|
Original pages: 545-571 289
и вообще Bh(z) при h 3 имеет вид |
|
2Fh+1−1zFh+2−1 + 2Fh+1−2Lh−1zFh+2 + сложные члены + 2h−1z2h−2 + z2h−1; |
(6) |
где Lk = Fk+1 + Fk−1. (Эта формула обобщает теорему A.) Число всех сбалансированных деревьев высоты h равно Bh = Bh(1) и удовлетворяет рекуррентному соотношению
B0 = B1 = 1; Bh+1 = Bh2 + 2BhBh−1; |
(7) |
так что B2 = 3, B3 = 3 5, B4 = 32 5 7, B5 = 33 52 7 23; в общем случае |
|
Bh = A0Fh A1Fh−1 : : :AhF−1 1 AhF0; |
(8) |
где A0 = 1, A1 = 3, A2 = 5, A3 = 7, A4 = 23, A5 = 347, : : :, Ah = Ah−1Bh−2 + 2. Последовательности Ah и Bh растут очень быстро, как ”экспонента экспоненты”; из упр. 7 следует, что существует действительное число 1:43684, такое, что
Bh = j 2h k − j 2h−1k + j 2h−2k − + (−1)hj 20 k: |
(9) |
Если считать, что все Bh деревьев равновероятны, то, как показывает упр. 8, среднее число узлов в дереве высоты h равно
h |
h |
(1) |
|
(0:70118) |
|
2h: |
(10) |
B0 (1)=B |
|
|
|
Это означает, что высота сбалансированного дерева с n узлами обычно гораздо ближе к log2 n, чем
кlog n.
Ксожалению, полученные результаты не имеют отношения к алгоритму A, так как механизм
этого алгоритма делает некоторые деревья гораздо более вероятными, чем другие. Рассмотрим, например, случай N = 7, когда существует 17 сбалансированных деревьев. Ключи можно вставлять 7! = 5040 различными способами, при этом идеально сбалансированное ”совершенное” дерево
Picture: рис. на стр. 546
получается 2160 раз. В противоположность этому фибоначчиево дерево
Picture: |
рис. на стр. 546 |
|
встречается лишь 144 |
раза, а похожее дерево |
|
Picture: |
рис. |
на стр. 546 |
встречается 216 раз. [Заменив левые поддеревья в (12) и (13) произвольными сбалансированными деревьями с четырьмя узлами и затем зеркально отразив относительно вертикальной оси, получим 16 различных деревьев; восемь деревьев, полученных из (12), встречаются по 144 раза, а полученные из (13)—по 216 раз. Довольно странно, что (13) встречается чаще, чем (12).]
Тот факт, что идеально сбалансированные деревья получаются с такой высокой вероятностью— совместно с формулой (10), соответствующей случаю равных вероятностей,—делает чрезвычайно правдоподобным соотношение: (среднее число сравнений при поиске по сбалансированному дереву) log2 N + c, где c—малая константа. Действительно, эмпирические проверки показывают, что среднее число сравнений, требуемых для вставки N-го элемента, при не слишком малых N примерно равно 1:01 log2 N + 0:1
Чтобы изучить поведение алгоритма A на фазах вставки и балансировки, можно классифицировать внешние узлы сбалансированного дерева, как показано на рис. 23. Путь, ведущий из внешнего узла вверх, описывается последовательностью плюсов и минусов (”+” для правой ссылки, ”-” для левой); мы выписываем ее, пока не достигнем первого узла с ненулевым показателем сбалансированности или (если таких узлов нет) пока не достигнем корня. Затем мы пишем A или B в соответствии с тем, будет ли новое дерево, полученное после вставки на данное место внутреннего узла, сбалансированным или нет. Так/путь из (3) вверх кодируется ++-B, что означает ”правая ссылка, правая ссылка, левая ссылка, несбалансировано”. Если код оканчивается на A, после вставки нового узла не
Picture: |
Рис. 23. Коды классификации, определяющие поведение алгоритма A после |
|
вставки. |

290Original pages: 545-571
требуется балансировки; код, оканчивающийся на ++B или --B, требует однократного поворота, а код, оканчивающийся на +-B или -+B, требует двукратного поворота. Если путь состоит из k звеньев, в шаге A6 корректируется ровно k − 1 показателей сбалансированности. Таким образом, описанные последовательности дают необходимую информацию о времени работы шагов A6–A10.
Эмпирические проверки со случайными числами в диапазоне100 N 2000дали приближенные вероятности для путей различных видов (табл. 1); очевидно, эти вероятности быстро приближаются к предельным значениям при N ! 1. В табл. 2 даны соответствующие точные вероятности (N = 10; все 10! перестановок считались равновероятными.)
Из табл. 1 мы видим, что вероятность события k 2 равна 0:144 + 0:153 + 0:144 + 0:144 = 0:585; таким образом, почти в 60% случаев шаг A6 тривиален. Среднее число изменений коэффициентов сбалансированности на этом шаге (0 заменяется на 1) примерно равно 1:8. Среднее число изменений коэффициентов сбалансированности от 1 до 0 в шагах A7–A10 равно 0:535 + 2(0:233 + 0:232) 1:5, т. е. вставка одного нового узла добавляет в среднем 0:3 несбалансированного узла. Это согласуется с тем фактом, что около 68% всех узлов в случайных деревьях, полученных с помощью алгоритма A, оказались сбалансированными.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Таблица 1 |
||
|
|
|
Приближенные вероятности при вставке N-го элемента |
||||||||||||||||
|
Длина пути |
Нет балансировки |
Однократный поворот Двукратный поворот |
||||||||||||||||
|
1 |
|
:144 |
|
:000 |
|
:000 |
|
|||||||||||
|
2 |
|
:153 |
|
:144 |
|
:144 |
|
|||||||||||
|
3 |
|
:093 |
|
:048 |
|
:048 |
|
|||||||||||
|
4 |
|
:058 |
|
:023 |
|
:023 |
|
|||||||||||
|
5 |
|
:036 |
|
:010 |
|
:010 |
|
|||||||||||
|
|
> 5 |
|
|
:051 |
|
:008 |
|
:007 |
|
|||||||||
ave |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2:78 |
:535 |
|
:233 |
|
:232 |
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Таблица 2 |
||
|
|
|
|
|
|
Точные вероятности при вставке 10-го элемента |
|
|
|
|
|||||||||
|
Длина пути |
Нет балансировки |
Однократный поворот Двукратный поворот |
||||||||||||||||
|
1 |
|
|
|
1=7 |
|
|
0 |
|
|
0 |
|
|
||||||
|
2 |
|
|
|
6=35 |
|
1=7 |
|
|
1=7 |
|
|
|||||||
|
3 |
|
|
|
4=21 |
|
2=35 |
|
2=35 |
|
|||||||||
|
|
4 |
|
|
|
|
0 |
|
|
|
1=21 |
|
1=21 |
|
|||||
ave |
247=105 |
|
53=105 |
26=105 |
26=105 |
Приближенная модель поведения алгоритма A была предложена К. К. Фостером [Proc. ACM Nat. Conf., 20, (1965), 192–205]. Модель эта не вполне корректна, но достаточно близка к истине, чтобы отразить существо дела. Предположим, что в большом дереве, построенном с помощью алгоритма A, показатель сбалансированности данного узла с вероятностью p равен 0; тогда этот показатель равен +1 с вероятностью 12(1 − p) и с той же вероятностью равен −1. Предположим далее (без всяких обоснований), что показатели сбалансированности всех узлов независимы. Тогда вероятность того, что шаг A6 делает ненулевыми ровно (k−1)показателей, равна pk−1(1−p), поэтому среднее значение k есть 1=(1−p). Вероятность того, что нужно повернуть часть дерева, равна 12. В среднем вставка нового узла должна увеличить число сбалансированных узлов ва p; это число в действительности увеличивается на 1 в шаге A5, на −p1(1 − p) в шаге A6, на 12 в шаге A7 и на 12 2 в шаге A8 или A9, так что мы получаем
|
|
1 |
|
|
||
|
p = 1 − p=(1 − p) + |
|
+ 1: |
|
||
2 |
|
|||||
Решение этого уравнения относительно p дает неплохое согласие с табл. 1: |
|
|||||
|
p |
|
|
|
|
|
p = |
9 −4 |
41 |
0:649; 1=(1 − p) 2:851: |
(14) |
||
Время работы фазы поиска программы A (строки 01–19) равно |
|
|||||
|
10C + C1 + 2D + 2 − 3S; |
(15) |
где C, C1, S—те же самые, что и в предыдущих алгоритмах этой главы, a D—число несбалансированных узлов, проходимых при поиске. Эмпирические проверки показывают, что можно положить