Добавил:
Rumpelstilzchen2018@yandex.ru Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3-й семестр / Лекция 3 - Бинарное дерево поиска

.pdf
Скачиваний:
36
Добавлен:
25.12.2020
Размер:
650.53 Кб
Скачать

Тема. ПОИСКОВЫЕ СТРУКТУРЫ ДАННЫХ. Бинарное дерево поиска

Поиск данных в структурах. Бинарное дерево поиска

Алгоритмы поиска значений в структурах данных имеющих линейную структуру и полностью располагающихся в оперативной памяти рассматривались в данном курсе в примерах.

Так алгоритм линейного поиска – это последовательный перебор элементов линейной структуры. Можно выделить два применения алгоритма, искать число вхождений заданного элемента (например, найти количество четных чисел в массиве) или найти первое вхождение значения в структуру (найти индекс первого отрицательного числа). Время поиска значения алгоритмом линейного поиска в худшем случае линейно зависит от количества элементов в структуре –

O(n).

Алгоритм бинарного поиска применяется для поиска в упорядоченных по некоторому значению структурах данных. Время поиска значения в структуре из n элементов логарифмически зависит от количества элементов в структуре –

О(log2 n).

Для оптимизации операции поиска в структурах данных, хранящихся на внешних носителях или просто больших по объему наборов данных хранящихся в оперативной памяти используются дополнительные структуры данных, назовем их поисковыми структурами. Так в базах данных для оптимизации поиска записей в таблицах используются индексные файлы – это отображение данных таблиц (части данных) в другие структуры данных, которые специальным образом организованы.

Предназначены для поиска данных в структурах, элементы которых имеют специальное поле, которое отличает их от других элементов. Такое поле называют ключом элемента. Ключом может служить номер зачетной книжки студента, для структур данных, используемых в вузе. К таким структурам относятся:

1.Бинарное дерево поиска.

2.AVL дерево

3.B – дерево

4.Хеш-таблица

1.Бинарное дерево поиска.

Используется для поиска данных снабженных ключом в структурах данных. Ключ – идентификатор к данным элемента структуры. При использовании

бинарного дерева поиска, ключи в пределах набора данных, должны быть уникальны.

Определение бинарного дерева поиска:

1

Это бинарное дерево, узлы которого упорядочены по значениям ключей по правилу:

слева от корня узлы со значением ключа меньше чем ключ в корне, а справа со значением ключа большим, чем в корне.

1.2. АТД Бинарное дерево поиска BinTreeSearch

Данные:

Список значений для узлов дерева с ключами Кол. Узлов

Root – корень дерева Операции:

//создание узла дерева

//TNode – тип узла дерева

//Tinfo – тип информационной части узла

//Tkey – тип ключа

//возвращает узел

Function CreateNode(key:Tkey; info:Tinfo):Tnode;

//вставить узел в бинарное дерево поиска согласно значению ключа

Procedure Ins (key:Tkey; info:Tinfo var T:BintreeSeach); //Удалить узел из дерева согласно значению ключа

Procedure Del(key:Tkey, var T:BintreeSeach);

//поиск элемента в дереве по значению переданного ключа //функция возвращает значение найденного узла дерева function search(key:Tkey; T:BintreeSeach);

//Возвращает true если дерево пусто и false в противном случае function Empty (var T:BintreeSeach):Boolean;

end.

1.3. Операции над бинарным деревом поиска

1.3.1. Вставка нового узла в дерево поиска

Так как дерево упорядочено по указанному правилу, узел вставляется в дерево согласно правила упорядочения.

Пример 1. Пусть требуется создать дерево поиска и включить в его узлы только ключи записей:

12, 3, 4, 15, 5, 14. На рисунке 1 представлена схема создания дерева поиска из заданных ключей.

Словесный алгоритм создания бинарного дерева поиска

1)Дерево пусто.

2)Узел с первым поступившим ключом – 12 будет вершиной дерева.

2

3) Вставка следующего узла связана с поиском места вставки: так как значение 3 меньше чем 12, которое находиться в корне, то его надо разместить в левом поддереве узла 12. Левого поддерева у узла 12 нет, следовательно, узел со значением 3 становиться левым поддеревом узла 12.

4)Вставка ключа 4. Поиск места вставки узла: так как 4 меньше 12, то его место в левом поддереве 12-ти, но у 12-ти левое поддерево есть, то сравниваем 4 с ключом узла этого поддерева – со значением 3. Так как 4 больше 3, то ключ 4 должен расположиться в правом поддереве узла со значением 3. У узла со значением 3 нет правого поддерева, поэтому 4 становиться вершиной правого поддерева узла 3.

5)Вставка ключа 15. Поиск места вставки узла: так как 15 больше ключа в корне дерева (12), то его место в правом поддереве дерева со значением 12. Так как правое поддерево узла 12 пусто, то ключ со значением 15 становиться значением вершины правого поддерева узла 12.

6)Вставка ключа 5. Место этого ключа в левом поддереве. Начинаем спускаться по левому поддереву узла со значение 12 и искать место для ключа 5. Это правое поддерево узла 4.

7)Вставка ключа 14. Место этого ключа в правом поддереве узла 12. Начинаем спуск по правому поддереву узла 12 и находим, что это значение должно стать левым узлом узла 14.

 

Дерево из одного

2)

 

 

 

 

 

12

5)

12

 

6)

 

12

 

12

12

3)

12

4)

 

 

 

 

 

 

узла

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3

 

 

 

3

 

 

3

15

 

3

 

 

 

 

 

 

3

 

 

15

 

 

 

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

 

 

 

4

 

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

14

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

Рис. 1. Визуализация алгоритма создания бинарного дерева поиска

1.3.2. Поиск в дереве поиска узла с заданным значением ключа

Алгоритм поиска напоминает алгоритм вставки узла. При вставке нового узла осуществляется спуск по ветви дерева для поиска места вставки узла согласно правила упорядочения узлов в дереве поиска. При поиске узла с заданным значением ключа так же надо спускаться по ветви дерева до тех пор, пока узел с заданным значением ключа не будет найден или ветвь не закончиться(ссылка на следующее поддерево станет равной nil) – т.е. ключа в дереве нет.

Пример 2. Найти узел с ключом 5 в созданном в примере 1 дереве.

Алгоритм.

3

Так как 5 меньше ключа в вершине дерева (12), то для поиска узла надо спускаться по левой ветви.

В вершине левого поддерева узла 12 находиться ключ 4, он меньше того, что ищем – 5. Следовательно, для поиска спускаемся по правой ветви узла 4. В

вершине правого поддерева узла со значением 4 и находим ключ 5. Т.е. найден узел и надо вернуть на него ссылку, т.е.значение правой ссылке дерева с корнем 4.

Пример 3. Поиск ключа не содержащегося в дереве.

Пусть ищем ключ со значение 6. Тогда поиск аналогичен примеру 2. Спускаемся по левому поддереву узла 12, затем по правому поддереву узла 3, затем узла 4 и

затем узла 5 и левая ветвь закончилась, следовательно, узла в дереве нет. В другой ветви его искать не надо, так как дерево упорядочено.

1.3.3. Удаление узла из дерева

Удаление узла из дерева сводиться к операциям:

Найти узел в дереве (получить на него ссылку)

Удалить узел. После удаления узла упорядоченность дерева должна сохраниться. Поэтому при удалении узла возможен один из 4-х случаев:

1.Удаляемый узел – лист

2.Удаляемый узел имеет правое поддерево, но не имеет левого.

3.Удаляемый узел имеет только левое поддерево, но не имеет правого.

4.Удаляемый узел имеет два поддерева.

Рассмотрим все случаи на примерах, чтобы понять суть алгоритма.

Пусть имеется дерево поиска, представленное на рисунке 2. Введем обозначения: D – удаляемый узел, R – замещающий узел (узел на который будет заменяться удаляемый узел в некоторых указанных случаях) , P – родитель удаляемого узла,

Pr – родитель замещающего узла

4

100

20 120

130

16

50

14

 

 

 

19

13

5

15

17

1.3.3.1. Удаляемый узел – лист

Удаляется узел (D) со значением 130. Это узел лист. Тогда необходимо просто у родителя этого узла установить в NULL ссылку на правое поддерево: P->.right = NULL. Замещающий узел в этом случае не используется.

1.3.3.2. Удаляемый узел имеет левое поддерево, но не имеет правого

Удаляется узел со значением 19. Этот узел имеет одно поддерево по левой ветви.

Сам узел 19 является правой ветвью узла 16, своего родителя (P), тогда удаление сводится к операции замещения удаляемого узла (D) его узлом в левой ветви:

R= D->left ; P->right=R.

5

1.3.3.3. Удаляемый узел имеет только правое поддерево, но не имеет левого

Удаляется узел со значением 50. Этот узел имеет одно поддерево по правой ветви. Сам узел 50 является правой ветвью узла 20, своего родителя (P), тогда удаление сводится к операции замещения удаляемого узла (D) его узлом в правой ветви: R= D->right ; P->right=R.

1.3.3.4. Удаляемый узел имеет два поддерева.

Удаляется узел со значением 20. У удаляемого узла D два поддерева. При поиске замещающего узла можно использовать 1 из 2-х подходов:

a) замещающий узел – max узел в левом поддереве удаляемого узла, т.е.

самый правый левого поддерева удаляемого узла.

b) замещающий - min узел правого поддерева удаляемого узла (т.е. самый левый правого поддерева удаляемого узла )

Рассмотрим алгоритм удаления a) который включает следующие действия:

Найти заменяемый узел R, спускаясь по левому поддереву удаляемого узла и найти самый правый узел .

Спуск предполагает 2 случая:

правое поддерево пусто.

правое поддерево не пусто, то подход завершается узлом, имеющим только левое поддерево или листом, если R лист.

Тогда для примера: D это - 20, его родитель Р-100, замещающий узел - самый правый в левом поддереве узла D это R – заменяющийся узел-19, Pr – родитель заменяющегося узла R это узел со значением 16 и R не лист, тогда алгоритм сводиться к операциям:

Pr=D->left;

6

R=Pr->right;

Pr->right=R->left;

D->data=R->data

Дерево до и после удаления значения 20 представлено на рисунке 3.

Примечание. Если R – лист, то алгоритм будет представлен действиями:

Pr->right=nil; R=D->left; D->data=R->data; P->right=R.

 

 

 

P

 

 

 

P

 

 

100

 

 

 

100

 

 

 

 

 

 

 

 

 

20

D

 

 

 

D

 

 

 

 

 

 

19

 

 

 

 

 

120

 

120

 

 

 

 

 

 

 

 

Pr

50

 

130

Pr

 

130

 

16

 

14

16

50

14

 

 

 

 

 

 

 

 

 

 

 

13

19

R

5

13

15

R

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

15

 

 

 

 

17

 

 

17

 

A) до удаления

 

 

 

Б) после удаления

 

 

 

 

 

 

Рис. 3. Дерево до и после удаления узла со значением 20

Реализация АТД BinTreeSeach

Представим реализацию дерева с использованием указателя.

Type

 

Tkey=byte;

//тип ключевого поля

Tinfo=integer;

//тип информационной части ключа

BintreeSeach =^TNode;

 

TNode=record

//структура узла дерева

key:Tkey;

 

info:Tinfo;

 

left, right: BintreeSeach;

//ссылка на левое и правое поддеревья

end;

 

//значение информационной части узла

7

function Value_Info(T:BintreeSeach):Tinfo; begin

result:=T^.info;

end;

//проверка дерева – не пусто ли дерево

Function Empty(var T:BintreeSeach):boolean; begin

result:=T=nil

end;

//Создание узла дерева

Function CreateNode(key:Tkey; info:Tinfo):BintreeSeach; Var

t: BintreeSeach; Begin

new(t); t^.info:=info;

t^.key:=key; t^.left:=nil;t^.right:=nil; result:=t;

end;

//Вставка нового узла в дерево

Procedure Ins (key:Tkey; info:Tinfo; var T:BintreeSeach);

begin

 

if t=nil then

 

t:= CreateNode(key,info)

// создание узла и включение его в дерево

else

//поиск места вставки в упорядоченном дереве

if key<t^.key then Ins(key,info,t^.left) else

if key>t^.key then Ins(key,info,t^.right) end;

//создание дерева, значения ключей и значений узла вводятся с клавиатуры procedure create(var t:BintreeSeach);

var info:Tinfo; key:Tkey;

begin repeat

read(key,info);

if info<>0 then ins(key,info,t); until info=0;

end;

8

//Вывод дерева

Procedure obxod(T:BintreeSeach;level,L:integer); Var

I:integer;

Begin

If not Empty(T) then Begin

obxod(T^.right,level+1,L); for i:=1 to level*L do write( ' ' ); writeln(T^.key); obxod(T^.left, level+1,L);

end

end;

//поиск узла со значением ключа. Функция возвращает указатель на узел или //nil, если узел не найден.

function search(key:Tkey; T:BintreeSeach): BintreeSeach; var f: BintreeSeach;

begin

if T<>nil then

if key<T^.key then result:=search(key, T^.left)

else

if key>T^.key then result:=search(key, T^.right)

else

if T^.key=key then result:=T

else result:=nil;

end;

//удаление из дерева узла с заданным значением ключа

Procedure DelTree(key:Tkey ; var T:BintreeSeach); var

q:BintreeSeach;

//спуск по правому поддереву узла, являющегося корнем левого поддерева //удаляемого узла

Procedure Del(var R,D:BintreeSeach); begin

If(R^.right<>nil) then

9

Del(R^.right, D)

Else begin

D^.key:=R^.key;

R:=R^.left

end

end;

begin

If(T<>nil)then If(key>T^.key) then

DelTree(Key, T^.right)

else

If(key<T^.key) then

DelTree (key, T^.left)

Else begin

q:=T;

If(q^.right=nil)then

T:=q^.left

Else

If(q^.left =nil)then T:=q^.right

Else Del(q^.left,q);

end;

end;

10