Avl - дерево: приближенно сбалансированное дерево
AVL - дерево: приближенно сбалансированное дерево
AVL-дерево - это дерево, обладающее следующими свойствами:
(1) Левое и правое поддеревья отличаются по глубине не более чем на 1.
(2) Оба поддерева являются AVL-деревьями.
Деревья, удовлетворяющие этому определению, могут быть слегка разбалансированными. Однако можно показать, что даже в худшем случае глубина AVL-дерева примерно пропорциональна log n, где n - число вершин дерева. Таким образом гарантируется логарифмический порядок производительности операций внутри, добавить и
удалить.Операции над AVL-деревом работают по существу так же, как и над двоичным справочником. В них только сделаны добавления, связанные с поддержанием приближенной сбалансированности дерева. Если после вставления или удаления дерево перестает быть приближенно сбалансированным, то специальные механизмы возвращают ему требуемую степень сбалансированности. Для того, чтобы эффективно реализовать этот механизм, нам придется сохранять некоторую дополнительную информацию относительно степени сбалансированности дерева. На самом деле, нам нужно знать только разность между глубинами поддеревьев, которая может принимать значения -1, 0 или +1. Тем не менее для простоты мы предпочтем сохранять сами величины
глубин поддеревьев, а не разности между ними.Мы определим отношение вставления элемента как
доб_avl( Дер, X, НовДер)
где оба дерева Дер и НовДер - это AVL-деревья, причем НовДер получено из Дер вставлением элемента X. AVL-деревья будем представлять как термы вида
д( Лев, А, Прав)/Глуб
где А - корень, Лев и Прав - поддеревья, а Глуб -
Двоично - троичные справочники
Двоично - троичные справочники
Двоичное дерево называют хорошо сбалансированным, если оба его поддерева имеют примерно одинаковую глубину (или размер) и сами сбалансированы. Глубина сбалансированного дерева приближенно равна log n , где n - число вершин дерева. Время, необходимое для вычислений, производимых отношениями внутри, добавить и удалить над двоичными справочниками, пропорционально глубине дерева. Таким образом, в случае двоичных справочников это время имеет порядок log n. Логарифмический рост сложности алгоритма, проверяющего принадлежность элемента множеству, - это определенное достижение по сравнению со списковым представлением, поскольку в последнем случае мы имеем линейный рост сложности
Усовершенствованные методы представления множеств деревьями
Глава 10. УСОВЕРШЕНСТВОВАННЫЕ МЕТОДЫ ПРЕДСТАВЛЕНИЯ МНОЖЕСТВ ДЕРЕВЬЯМИ
В данной главе мы рассмотрим усовершенствованные методы представления множеств при помощи деревьев. Основная идея состоит в том, чтобы поддерживать сбалансированности или приближенную сбалансированность дерева, с тем чтобы избежать вырождения его в список. Механизмы балансировки деревьев гарантируют, даже в худшем случае, относительно быстрый доступ к элементам данных, хранящихся в дереве, при логарифмическом порядке времени доступа. В этой главе изложено два таких механизма: двоично-троичные (кратко, 2-3) деревья и AVL-деревья. (Для изучения остальных глав понимание данной главы не обязательно.)
double_line();Литература
Литература
2-3 деревья детально описаны, например, в Aho, Hopcroft and Ullman (1974, 1983). В книге этих авторов, вышедшей в 1983 г., дается также реализация соответствующих алгоритмов на языке Паскаль. Н.Вирт (см. Wirth (1976)) приводит программу на Паскале для работы с AVL-деревьями. 2-3 деревья являются частным случаем более общего понятия В-деревьев. В-деревья, а также несколько других вариантов структур данных, имеющих отношение к 2-3 деревьям в AVL-деревьям, рассматриваются в книге Gonnet (1984). В этой книге, кроме того, даны результаты анализа поведения этих структур.
Программа вставления элемента в AVL-дерево, использующая только величину "перекоса" дерева (т.е. значение разности глубин поддеревьев, равной -1, 0 или 1, вместо самой глубины) опубликована ван Эмденом (1981).
Aho А. V., Hopcroft J. Е. and Ullman J. D. (1974). The Design and Analysis of Computer Algorithms. Addison-Wesley. [Имеется перевод: Ахо А., Хопкрофт Дж. Построение и анализ вычислительных алгоритмов. Пер. с англ. - М.: Мир, 1979.]
Aho А. V., Hopcroft J. Е. and Ullman J. D. (1983). Data Structures and Algorithms. Addison-Wesley.
Gonnet G. H. (1984). Handbook of Algorithms + Data Structures. Addison-Wesley.
van Emden M. (1981). Logic Programming Newsletter 2.
Wirth N. (1976). Algorithms + Data Structures = Programs. Prentice-Hall. [Имеется перевод: Вирт H. Алгоритмы + структуры данных = программы. - M.: Мир, 1985.]
Некоторые из случаев работы отношения...
Некоторые из случаев работы отношения встав.
(a) встав( в2( Д1, М, Д2), X, в2( НД1, М, Д2) );
(b) встав( в2( Д1, М, Д2), X,
в3( НД1а, Мб, НД1б, М, Д2) );
(c) встав( в3( Д1, М2, Д2, М3, Д3), X,
в2( НД1а, Мб, НД1б), М2, в2( Д2, М3, Д3) ).
line();
% Вставление элемента в 2-3 справочник
доб23( Дер, X, Дер1) :-
% Вставить Х в Дер, получить Дер1
встав( Дер, X, Дер1).
% Дерево растет вширь
доб23( Дер, X, в2( Д1, М2, Д2) ) :-
встав( Дер, X, Д1, М2, Д2). % Дерево растет вглубь
доб23( nil, X, л( Х) ).
встав( л( А), X, л( А), X, л( Х) ) :-
больше( X, А).
встав( л( А), X, л( Х), А, л( А) ) :-
больше( А, X).
встав( в2( Д1, М, Д2), X, в2( НД1, М, Д2) ) :-
больше( М, X),
встав( Д1, X, НД1).
встав( в2( Д1, М, Д2), Х, в3( НД1а, Мб, НД1б, М, Д2) ) :-
больше( М, X),
встав( Д1, X, НД1а, Мб, НД1б).
встав( в2( Д1, М, Д2), X, в2( Д1, М, НД2) ) :-
больше( X, М),
встав( Д2, X, НД2).
встав( в2( Д1, М, Д2), Х, в3( Д1, М, НД2а, Мб, НД2б) ) :-
больше( X, М),
встав( Д2, X, НД2а, Мб, НД2б).
встав( в3( Д1, М2, Д2, М3, Д3), Х, в3( НД1, М2, Д2, М3, Д3) :-
больше( М2, X),
встав( Д1, X, НД1).
встав( в3( Д1, М2, Д2, М3, Д3), X,
в2( НД1а, Мб, НД1б), М2, в2( Д2, М3, Д3) ) :-
больше( М2, X),
встав( Д1, X, НД1а, Мб, НД1б).
встав( в3( Д1, М2, Д2, М3, Д3), X,
в3( Д1, М2, НД2, М3, Д3) ) :-
больше( X, М2), больше( М3, X),
встав( Д2, X, НД2).
встав( в3( Д1, М2, Д2, М3, Д3), X,
в2( Д1, М2, НД2а), Мб, в2( НД2б, М3, Д3) ) :-
больше( X, М2), больше( М3, X),
встав( Д2, X, НД2а, Мб, НД2б).
встав( в3( Д1, М2, Д2, М3, Д3), X,
в3( Д1, М2, Д2, М3, НД3) ) :-
больше( X, М3),
встав( Д3, X, НД3).
встав( в3( Д1, М2, Д2, М3, Д3), X,
в2( Д1, М2, Д2), М3, в2( НД3а, Мб, НД3б) ) :-
больше( X, М3),
встав( Д3, X, НД3а, Мб, НД3б).
Объекты, показанные на рисунке, удовлетворяют отношению встав( дер, 6, нда, мб, ндб).
Объекты, показанные на рисунке, удовлетворяют отношению встав( Дер, 6, НДа, Мб, НДб).
2-3 деревья мы будем представлять в программе следующим образом: nil представляет пустое дерево; л( Х) представляет дерево, состоящее из одной вершины - листа с элементом X; в2( Д1, М, Д2) представляет дерево с двумя поддеревьями Д1 и Д2; М - минимальный элемент из Д2; в3( Д1, М2, Д2, М3, Д3) представляет дерево с тремя поддеревьями Д1, Д2 и Д3; М2 - минимальный элемент из Д2; М3 - минимальный элемент из Д3; Д1, Д2 и Д3 - 2-3 деревья.
Между доб23 и встав существует следующая связь: если после вставления нового элемента дерево не "вырастает", то
доб23( Дер, X, НовДер) :-
встав( Дер, X, НовДер).
Однако если после вставления элемента глубина дерева увеличивается, то встав порождает два поддерева Д1 и Д2, а затем составляет из них дерево большей глубины:
доб23( Дер, X, в2( Д1, М, Д2) ) :-
встав( Дер, X, Д1, М, Д2).
Отношение встав устроено более сложным образом, поскольку ему приходится иметь дело со многими случаями, а именно вставление в пустое дерево, в дерево, состоящее из одного листа, и в деревья типов в2 и в3. Возникают также дополнительные подслучаи, так как новый элемент можно вставить в первое, либо во второе, либо в третье поддерево. В связи с этим мы определим встав как набор правил таким образом, чтобы каждое предложение процедуры встав имело дело с одним из этих случаев. На рис. 10.5 показаны некоторые из возможных случаев. На Пролог они транслируются следующим образом:
Случай а
встав( в2( Д1, М, Д2), X, в2( НД1, М, Д2) ) :-
больше( М, X),
% М больше, чем Х
встав( Д1, X, НД1).
Случай b
встав( в2( Д1, M, Д2), X, в3( НД1а, Мб, НД1б, M, Д2) ) :-
больше( М, X),
встав( Д1, X, НД1а, Мб, НД1б).
Случай с
встав( в3( Д1, М2, Д2, М3, Д3), X, в2( НД1а, Мб, НД1б), М2, в2(Д2, М3, Д3) ) :-
больше( М2, X),
встав( Д1, X, НД1а, Мб, НД1б).
Отмеченный путь показывает процесс поиска элемента 10.
2-3 справочник. Отмеченный путь показывает процесс поиска элемента 10.
При поиске элемента Х в 2-3 справочнике мы начинаем с корня и двигаемся в направлении самого нижнего уровня, руководствуясь при этом метками внутренних вершин дерева. Пусть корень содержит метки Ml и М2, тогда если Х < M1, то поиск продолжается в левом поддереве, иначе если Х < М2, то поиск продолжается в среднем поддереве, иначе - в правом поддереве.
Если в корне находится только одна метка М, то переходим к левому поддереву при Х < М и к правому поддереву - в противоположном случае. Продолжаем применять сформулированные выше правила, пока не окажемся на самом нижнем уровне дерева, где и выяснится, найден ли элемент X, или же поиск потерпел неудачу.
Так как все листья 2-3 дерева находятся на одном и том же уровне, 2-3 дерево идеально сбалансировано с точки зрения глубины составляющих его поддеревьев. Все пути от корня до листа, которые мы проходим при поиске, имеют одну и ту же длину порядка log n, где n - число элементов, хранящихся в дереве.
При добавлении нового элемента данных 2-3 дерево может расти не только в глубину, но и в ширину. Каждая внутренняя вершина, имеющая два поддерева, может приобрести новое поддерево, что приводит к росту вширь. Если же, с другой стороны, у вершины уже есть три поддерева, и она должна принять еще одно, то она расщепляется на две вершины, каждая из которых берет на себя по два из имеющихся четырех поддеревьев. Образовавшаяся при этом новая вершина передается вверх по дереву для присоединения к одной из выше расположенных вершин. Если же эта ситуация возникает на самом высоком уровне, то дерево вынуждено "вырасти" на один уровень вверх. Рис. 10.3 иллюстрирует описанный принцип.
Полностью разбалансированный двоичный справочник.
Полностью разбалансированный двоичный справочник.
Производительность его та же, что и у списка.
с ростом размера множества. Однако плохая сбалансированность дерева ведет к деградации производительности алгоритмов, работающие со справочником. В крайнем случае, двоичный справочник вырождается в список, как показано на рис.10. l. Форма справочника зависит от той последовательности, а которой в всего записываются элементы данных. В лучшей случае мы получаем хорошую балансировку и производительность порядка log n, а в худшем - производительность будет порядка n. Анализ показывает, что в среднем сложность алгоритмов внутри, добавить и удалить сохраняет порядок log n в допущении, что все возможные входные последовательности равновероятны. Таким образом, средняя производительность, к счастью, оказывается ближе к лучшему случаю, чек к худшему. Существует, однако, несколько довольно простых механизмов, которые поддерживают хорошую сбалансированность дерева, вне зависимости от входной последовательности, формирующей дерево. Эти механизмы гарантируют производительность алгоритмов внутри, добавить и удалить порядка log n даже в худшем случае. Один из этих механизмов - двоично-троичные деревья (кратко, 2-3 деревья), а другой - AVL-деревья.
2-3 дерево определяется следующим образом: оно или пусто, или состоит из единственной вершины, или удовлетворяет следующим условиям:
каждая внутренняя вершина имеет две или три дочерних вершины, и
все листья дерева находятся на одном и том же уровне.
Двоично-троичным (2-3) справочником называется 2-3 дерево, все элементы данных которого хранятся в листьях и упорядочены слева направо. На рис. 10.2 показан пример. Внутренние вершины содержат метки, равные минимальным элементам тех или иных своих поддеревьев, в соответствии со следующими правилами:
если внутренняя вершина имеет два поддерева, то она содержит минимальный элемент второго из них;
если внутренняя вершина имеет три поддерева, то она содержит минимальные элементы второго и третьего поддеревьев.
Сбалансированные или приближенно сбалансированные деревья
Резюме
2-3 деревья и AVL-деревья, представленные в настоящей главе, - это примеры сбалансированных
деревьев.
Сбалансированные или приближенно сбалансированные деревья гарантируют эффективное выполнение трех основных операций над деревьями: поиск, добавление и удаление элемента. Время выполнения этих операций пропорционально log n, где n - число вершин дерева.
Три правила построения нового avl-дepевa.
Три правила построения нового AVL-дepевa.
глубину h +1.
Дер1, А, Дер2, В, Дер3
Рассмотрим три случая:
(1) Среднее дерево Дер2 глубже остальных двух деревьев.
(2) Дер1 имеет глубину не меньше, чем Дер2 и Дер3.
(3) Дер3 имеет глубину не меньше, чем Дер2 и Дер1.
На рис. 10.9 видно, как можно построить дерево НовДер в каждом из этих трех случаев. Например, в случае 1 среднее дерево Дер2 следует разбить на два части, а затем включить их в состав НовДер. Три правила, показанные на pис.10.9, нетрудно запасать на Прологе в виде отношения
соединить( Дер, А, Дер2, В, Дер3, НовДер)
Последний аргумент НовДер - это AVL-дерево, построенное из пяти составных частей, пяти первых аргументов. Правило 1, например, принимает вид:
соединить( Д1/Н1, А, д( Д21, В, Д22)/Н2, С, Д3/Н3,
% Пять частей
д( д( Д1/Н1, А, Д21)/На, В, д( Д22, С, Д3/Н3)/Нс)/Нb) :-
% Результат
H2 > H1, H2 > Н3,
% Среднее дерево глубже остальных
На is Н1 + 1,
% Глубина левого поддерева
Нс is Н3 + 1,
% Глубина правого поддерева
Hb is На + 1,
% Глубина всего дерева
Программа доб_аvl, вычисляющая также глубину дерева и его поддеревьев, показана полностью на рис. 10.10.
изменения для устранения лишних
Упражнения
Определите отношение
внутри( Эдем, Дер)
для поиска элемента Элем в 2-3 справочнике Дер.
Посмотреть ответ
Введите в программу рис. 10. 6 изменения для устранения лишних возвратов (определите отношения встав2 и соединить).
line(); % Отображение 2-3 справочников
отобр(Д) :- 15
отобр( Д, 0). --
отобр( nil, _ ). 15
отобр( л(А), Н) :- --
tab( H), write( A), nl. 13
отобр( в2( Д1, М, Д2), Н) :- --
H1 is H + 5 13
отобр( Д2, H1), --
tab( H), write( --), nl, 12
tab( H), write( M), nl, --
tab( H), write( --), nl, 12
отобр( Д1, H1). 10
отобр( в3( Д1, M2, Д2, М3, Д3), H) :- 10
H1 is H + 5 --
отобр( Д3, H1), 8
tab( H), write( --), nl, --
tab( H), write( M3), nl, 8
отобр( Д2, H1), --
tab( H), write( M2), nl, 7
tab( H), write( --), nl, --
отобр( Д1, H1). 7
--
(a) 5
-- 5
--
4
--
4
3
3
--
1
(б)
line();
Вставление элемента в 2-3 справочник. В этой
Вставление элемента в 2-3 справочник. В этой
программе предусмотрено, что попытка повторного
вставления элемента терпит неудачу.
Программа для вставления нового элемента в 2-3 справочник показана полностью на рис. 10.6. На рис. 10.7 показана программа вывода на печать 2-3 деревьев.
Наша программа иногда выполняет лишние возвраты. Так, если встав с тремя аргументами терпит неудачу, то вызывается процедура встав с пятью аргументами, которая часть работы делает повторно. Можно устранить источник неэффективности, если, например, переопределить встав как
встав2( Дер, X, Деревья)
где Деревья - список, состоящий либо из одного, либо из трех аргументов:
Деревья = [ НовДер], если
встав( Дер, X, НовДер)
Деревья = [ НДа, Мб, НДб],
если встав( Дер, X, НДа, Мб, НДб)
Теперь отношение доб23 можно переопределить так:
доб23( Д, X, Д1) :-
встав( Д, X, Деревья),
соединить( Деревья, Д1).
Отношение соединить формирует одно дерево Д1 из деревьев, находящихся в списке Деревья.
Вставление элемента в avl-справочник.
Вставление элемента в AVL-справочник. В этой
программе предусмотрено, что попытка повторного вставления
элемента терпит неудачу. По поводу процедуры соединить см.
рис. 10.9.
деревья представляйте в виде термов
д( Лев, Кор, Прав) или nil.
Посмотреть ответ
Вставление нового элемента в 2-3 справочник. Дерево
Вставление нового элемента в 2-3 справочник. Дерево
растет сначала вширь, а затем уже вглубь.
Включение нового элемента в 2-3 справочник мы запрограммируем как отношение
доб23( Дер, X, НовДер)
где дерево НовДер получено введением элемента Х в дерево Дер. Основную работу мы поручим двум дополнительным отношениям, которые мы назовем встав. Первое из них имеет три аргумента:
встав( Дер, X, НовДер).
Здесь НовДер - результат вставления элемента Х в Дер. Деревья Дер и НовДер имеют одну и ту же глубину. Разумеется, не всегда возможно сохранить ту же глубину дерева. Поэтому существует еще одно отношение с пятью аргументами специально для этого случая:
встав( Дер, X, НДа, Mб, НДб).
Имеется в виду, что при вставления Х в Дер дерево Дер разбивается на два дерева НДа и НДб, имеющих ту же глубину, что и Дер. Мб - это минимальный элемент из НДб. Пример показан на рис. 10.4.
Задача вставления элемента в avl-справочник...
Задача вставления элемента в AVL-справочник
(a) AVL-дерево перед вставлением Х, Х > А;
(b) AVL-дерево после вставления Х в R;
(с) составные части, из которых следует построить новое дерево.
глубина дерева. Пустое дерево изображается как nil/0. Теперь рассмотрим вставление элемента Х в непустой AVL-справочник
Дер = д( L, A, R)/H
Начнем со случая, когда Х больше А. Х необходимо вставить в R, поэтому имеем следующее отношение:
доб_аv1( R, X, д( R1, В, R2)/Hb)
На рис. 10.8 показаны составные части, из которых строится дерево НовДер:
L, А, R1, В, R2
Какова глубина деревьев L, R, R1 и R2? L и R могут отличаться по глубине не более, чем на 1. На рис. 10.8 видно, какую глубину могут иметь R1 и R2. Поскольку в R был добавлен только один элемент X, только одно из поддеревьев R1, R2 может иметь