Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Casep^.iBalance of

Begin

Else

End

Begin

Begin

Var

Else

Begin

Else

End

Begin

Else

End

Begin

Else

End

Begin

Else

Else

Begin

Var

Begin

Begin

End.

Begin

Begin

Begin

Var

Begin

New(PCurr);

Readln(F, pCurr^.iKey);

pCurr^.pLeft:= Nil;

pCurr^.pRight:= Nil;

Inc(nItem);

SetLength(mItem, nItem);

mItem[nItem - 1]:= pCurr;

end;

 

CloseFile(F);

end;


procedure MakeTree(var pFrom: pMyNodeType; iFrom, iTo: integer);

i: integer;

if iFrom > iTo then Exit;

 

i:= (iFrom + iTo) div 2;

 

pFrom:= mItem[i];

 

MakeTree(pFrom^.pLeft, iFrom, i - 1);

MakeTree(pFrom^.pRight, i + 1, iTo);

end;

 


procedure Detour(pStart: pMyNodeType; Level: integer);

var i: integer;

for i:=1 to Level do Write(Chr(32));

WriteLN(pStart^.iKey);

Detour(pStart^.pLeft, Level + 1);

Detour(pStart^.pRight, Level + 1);

end;

 

pRoot:= Nil;

ReadItems;

 

MakeTree(pRoot, 0, nItem-1);

 

Detour(pRoot, 0).

if pFrom = nil then

New(pFrom);

pFrom^.iKey:= iKey;

pFrom^.pLeft:= nil;

pFrom^.pRight:= nil;

Exit;

end;

 

if iKey = pFrom^.iKey then Halt;

 

if iKey < pFrom^.iKey then

IncludeKey(pFrom^.pLeft, iKey)

else // iKey > pFrom^.iKey

IncludeKey(pFrom^.pRight, iKey)

end;

Исключение из двоичного дерева поиска

 

Для удаления существующего ключа применяется рекурсивно вызываемая процедура ExcludeKey. Она проникает вглубь дерева поиска, пытаясь отыскать в дереве предлагаемый к удалению ключ. Если дерево пусто, приложение аварийно завершается.

При проникновении в дерево отношение «больше – меньше» используется для поиска того места, где удаляемый ключ должен быть, если дерево является деревом поиска. Если та ссылка, по которой далее следует искать место для iKey, указывает в «никуда» (pFrom = nil), приложение аварийно завершается.


Если ключ (узел pFrom), который нужно удалить, найден, рассматривается четыре возможных ситуации с его потомками.

1. Обоих потомков нет. Ссылку от узла-предка обнуляем, а сам удаляемый узел выбрасываем из памяти.

2. Есть только левый потомок. Ссылку от узла-предка перенацеливаем на этого левого потомка, а сам удаляемый узел выбрасываем из памяти.

3. Есть только правый потомок. Ссылку от узла-предка перенацеливаем на этого правого потомка, а сам удаляемый узел выбрасываем из памяти.

 

4. Есть оба потомка. Углубляемся в левое поддерево, разыскиваем в нем самый правый из левых потомков (СПЛП). Когда поиск завершается, обмениваем значениями ключи в СПЛП и pFrom. Возникает случай 2.

 


procedure ExcludeKey(var pFrom: pMyNodeType; iKey: integer);

pCurr, pPrev: pMyNodeType;

if pFrom = nil then Halt;

 

if iKey < pFrom^.iKey then

ExcludeKey(pFrom^.pLeft, iKey)

if iKey > pFrom^.iKey then

ExcludeKey(pFrom^.pRight, iKey)


// Сюда попали => мы на том самом узле и ключе, который нужно удалить

begin // Удаляем именно этот узел

if (pFrom^.pLeft = nil) and (pFrom^.pRight = nil) then

Dispose(pFrom);

pFrom:= nil;

if pFrom^.pLeft = nil then // Есть только правое поддерево

pCurr:= pFrom^.pRight; // <> nil

Dispose(pFrom);

pFrom:= pCurr;


if pFrom^.pRight = nil then // Есть только левое поддерево

pCurr:= pFrom^.pLeft; // <> nil

Dispose(pFrom);

pFrom:= pCurr;


begin // Есть ОБА поддерева

pCurr:= pFrom^.pLeft; // <> nil

pPrev:= nil;

while pCurr^.pRight <> nil do

pPrev:= pCurr;

pCurr:= pCurr^.pRight;

end;

 

pFrom^.iKey:= pCurr^.iKey;

 

if pPrev = nil then

pFrom^.pLeft:= pCurr^.pLeft

pPrev^.pRight:= pCurr^.pLeft;

Dispose(pCurr);

end; // Есть ОБА поддерева

end; // Удаляем именно этот узел

end;

 

 


Графическое представление деревьев

 

На данном этапе графическое представление будет использовано только для деревьев поиска.

Графическое представление дерева может быть организовано несколькими способами. Первый, самый простой, показан на Рисунке 1.

Рис.1


Узлы для каждого значения глубины (глубины 0 для узла «44», глубины 1 для узлов «22», «46», и т.д.) располагаются, начиная с меньшего ключа, прижатого к левому краю рисунка, и далее в порядке роста ключей на данной глубине. Если через узлы провести вертикальные линии, на самой левой вертикали будет Располагаться больше всего узлов, на второй вертикали – меньше, на третьей, возможно, еще меньше (во всяком случае, не больше), и т.д.

Этот способ представления, кроме простоты, других достоинств не имеет.

Недостатки:

1. Дерево выглядит некрасиво. Чем больше глубина (высота) дерева, тем более вытянутыми становятся линии связи от предка к потомку.

2. Левые потомки узла могут оказаться «правее» этого узла. Как, например, левый потомок «45» узла «46».

3. Правые потомки узла могут оказаться «левее» этого узла. Как, например, правый потомок «51» узла «48».

Таким образом, это графическое представление не вполне согласуется с принципами дерева поиска «Всё, что левее – меньше, всё, что правее – больше».

 

На Рисунке 2 такой принцип уже полностью соблюден. И выглядит дерево вполне пристойно. Оно, можно сказать, похоже на настоящее дерево. Конечно, линии связи между «неглубокими» (близкими к корню) узлами длинноваты, но это неизбежно. Зеленым цветом показан узел, возникший последним.

 

Рис.2


Для ключей такого дерева справедливо утверждение: все узлы, расположенные справа от данного узла, имеют ключи, большие, чем на данном узле, а все узлы, расположенные слева от данного узла, имеют ключи, меньшие, чем на данном узле. Причем, это относится не только к узлам-потомкам данного узла, но и ко всем остальным узлам. Следовательно, можно утверждать: каждый узел занимает свою «индивидуальную» вертикаль, второго узла на этой же вертикали нет. При любой перестройке дерева с данным набором ключей каждый из узлов может перейти на другую глубину, сменить потомков и/или предков, но вертикаль свою не покинет.

Теперь пора обсудить, как такое дерево изобразить.

Каждый прямоугольник, изображающий узел, характеризуется координатами верхнего левого угла (полями X и Y). Ширина прямоугольника представлена полем iBoxWidth, она для каждого ключа своя (символы в ключе имеют каждый свою ширину, да и количества символов в ключе могут быть разными). Y-координата может управляться глубиной узла, которая является одним из параметров функции Detour («Обход»). X-координата должна зависеть от порядкового номера «индивидуальной» вертикали, а значит, от порядкового номера ключа. Это означает, что ключи перед их прорисовкой на экране должны быть отсортированы в порядке возрастания. Это делается с помощью организации второй, «параллельной» структуры данных (из тех же ключей) в виде линейного однонаправленного отсортированного списка. Постановка каждого нового ключа в сортированный список рассматривалась на предыдущих лекциях, поэтому об организации линейного списка здесь будет сказано кратко.

Итак, для доступа к основной структуре «Дерево» требуется глобальная переменная pRoot – указатель на корневой узел дерева. Связными для дерева являются поля pLeft и pRight.

Для доступа к параллельной структуре «Линейный список» требуется глобальная переменная pHead – указатель на узел с меньшим ключом. Связным для списка является поле pNext.

Конечно, атрибуты параллельной структуры «отягощают» дерево. Но следует помнить, что они вводятся здесь только для визуализации дерева в рамках учебного процесса. Визуализировать дерево в промышленных масштабах вряд ли придется.

 


Включение в сбалансированное дерево

 

pMyNodeType = ^MyKnot;

 

MyKnot = record

iKey: longint;

pLeft, pRight: pMyNodeType;

iBalance: integer;

end;

 

// iBalance = <Высота правого поддерева> – < Высота левого поддерева >

Пусть hL – высота левого поддерева, hR – высота правого поддерева.

 

Возможны следующие ситуации при включении очередного узла в сбалансированное дерево.

0. Новый узел не изменил высоту ни левого, ни правого поддерева. Баланс не нарушен.

1. Новый узел был включен в левое поддерево и увеличил его высоту на единицу.

1(а). До включения было hL < hR (т.е. hL = hR 1), => после включения стало hL = hR, баланс даже улучшился.

1(б). До включения было hL = hR, => после включения стало hL = hR + 1, баланс не нарушился (дерево не перестало быть сбалансированным).

1(в). До включения было hL > hR (т.е. hL = hR + 1), => после включения стало hL = hR + 2, требуется восстановление баланса. Способы восстановления показаны на Рис. 1 и Рис. 2.


 

 

Было Стало

Рис.1

 

Было Стало

Рис.2

2. Новый узел был включен в правое поддерево и увеличил его высоту на единицу.

2(а). До включения было hL > hR (т.е. hL = hR + 1), => после включения стало hL = hR, баланс даже улучшился.

2(б). До включения было hL = hR, => после включения стало hL = hR - 1, баланс не нарушился (дерево не перестало быть сбалансированным).

2(в). До включения было hL < hR (т.е. hL = hR 1), => после включения стало hL = hR 2, требуется восстановление баланса. Способы восстановления показаны на Рис. 3 и Рис. 4.


 

 

Было Стало

Рис.3

 

Было Стало

Рис.4

 


procedure IncludeKey(var p: pMyNodeType; iKey: integer; var bBecameHigher: boolean);

p1, p2: pMyNodeType;

if p = nil then

New(p);

bBecameHigher:= true; // Tree became higher

p^.iKey:= iKey;

p^.pLeft:= nil;

p^.pRight:= nil;

p^.iBalance:= 0;


if iKey < p^.iKey then

IncludeKey(iKey, p^.pLeft, bBecameHigher);

 

if bBecameHigher then // Left branch became higher

+1:

<== предыдущая лекция | следующая лекция ==>
 | Casep^.iBalance of. bBecameHigher := false;
Поделиться с друзьями:


Дата добавления: 2014-01-07; Просмотров: 275; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.055 сек.