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

сиаод / Otvety_Saod

.pdf
Скачиваний:
121
Добавлен:
11.05.2015
Размер:
4.11 Mб
Скачать

19.Не рекурсивный обход n-арного дерева.

Замена рекурсии циклом основана на использовании вспомогательного стека для хранения последовательности пройденных вершин от корня до текущей вершины

Не рекурсивный алгоритм обхода (стековый обход) дерева в прямом порядке:

Пусть T – указатель на бинарное дерево; А – стек, в который заносятся адреса еще не пройденных вершин; TOP – вершина стека; P – рабочая переменная.

1.Начальная установка: TOP:=0; P:=T.

2.Если P=nil, то перейти на 4. {конец ветви}

3.Вывести P^.info. Вершину заносим в стек: TOP:=TOP+1; A[TOP]:=P; шаг по левой ветви: P:=P^.llink; перейти на 2.

4.Если TOP=0, то КОНЕЦ.

5.Достаем вершину из стека: P:=A[TOP]; TOP:=TOP-1; Шаг по правой связи: P:=P^.rlink; перейти на 2.

20.Поиск в упорядоченных таблицах. Последовательный поиск в массиве.

Очень часто приходится решать следующую задачу: найти элемент с нужным значением ключа. Эту задачу приходится решать с использованием массивов, списков, деревьев. Эта задача решается по-разному в случае неупорядоченных или упорядоченных по возрастанию ключей элементов. В последнем случае можно не просматривать все элементы, а доходить только до элемента с ключом большим, чем требуемый.

Существует два альтернативных способа поиска: последовательный и двоичный. В последовательном списке может применяться только последовательный поиск в порядке следования указателей. В двоичном дереве естественным является двоичный поиск. В массиве может быть организован как тот, так и другой алгоритм.

Поставим задачу так: найти наименьший индекс (указатель) компоненты списка со значением ключа, равным Х.

Последовательный поиск в массиве

Пусть массив:

VAR A: ARRAY[1..N] OF INTEGER; X: INTEGER;

FUNCTION LOC(X: INTEGER): INTEGER; VAR I: INTEGER;

BEGIN

I := 0;

REPEAT I := I+ 1

UNTIL (A[I] = X) OR (I = N);

IF A[I] <> X THEN LOC := 0 ELSE LOC := I

END;

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

Неудобство функции в том, что проверка условия окончания сложная. Программу можно ускорить, если поставить "барьер", т.е. увеличить массив на один элемент и заслать туда значение искомого ключа. I := 0; A[N+1] := X;

REPEAT I := I + 1;

UNTIL A[ I ] = X;

IF I > N THEN LOC := 0 ELSE LOC := I;

Среднее число просмотров m = N / 2.

21.Поиск в упорядоченных таблицах. Двоичный поиск в массиве. Фибоначчиев поиск. Интерполяционный поиск.

Очень часто приходится решать следующую задачу: найти элемент с нужным значением ключа. Эту задачу приходится решать с использованием массивов, списков, деревьев. Эта задача решается по-разному в случае неупорядоченных или упорядоченных по возрастанию ключей элементов. В последнем случае можно не просматривать все элементы, а доходить только до элемента с ключом большим, чем требуемый.

Существует два альтернативных способа поиска: последовательный и двоичный. В последовательном списке может применяться только последовательный поиск в порядке следования указателей. В двоичном дереве естественным является двоичный поиск. В массиве может быть организован как тот, так и другой алгоритм.

Двоичный поиск:

Применяется для упорядоченных массивов. При двоичном поиске используется метод деления пополам. Поставим задачу так: найти наименьший индекс (указатель) компоненты списка со значением ключа, равным Х.

Алгоритм:

VAR A: ARRAY[1..N] OF INTEGER; X: INTEGER;

VAR I: INTEGER; BEGIN

I := 1; J := N; REPEAT

K := (I + J) DIV 2;

IF X > A[K] THEN I := K + 1 ELSE J := K - 1 UNTIL (A[K] = X) OR (I > J)

END.

Число просмотров m = log2N.

При N = 100: последовательный поиск выполнится за 50 просмотров (m = 50), двоичный за 6,62 (m = 6,62)

22. Поиск в линейном списке.

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

Самое простое решение:

WHILE (P <> Nil) AND (P^.KEY <> X) DO P := P^.NEXT

Если элемента в списке нет, то при Р равном Nil нет следующего элемента P^.KEY. Поэтому надо ввести вторую часть проверки

WHILE P<> Nil DO

IF P^.KEY = X THEN { выход из цикла }

ELSE P := P^.NEXT;

Лучше воспользоваться логической переменной (флажком):

B := TRUE;

WHILE (P <> Nil) AND B DO

IF P^.KEY = X THEN B := FALSE ELSE P := P^.NEXT;

Элемент не найден, если P = Nil и B = TRUE.

23. Двоичное дерево поиска. Свойства. Основные операции.

Если для каждого узла ti дерева все ключи в левом поддереве меньше ключа ti, а все ключи в правом поддереве больше ключа ti, то это дерево упорядочено по ключам и называется деревом поиска. В дереве поиска можно найти место каждого ключа, двигаясь от корня и переходя на левое или правое поддерево каждого узла в зависимости от значения его ключа. Для поиска среди N элементов может потребоваться не более log2N сравнений, если дерево идеально сбалансировано.

В этом преимущество дерева перед линейным списком, в котором для поиска может потребоваться N сравнений. Поиск в упорядоченном двоичном дереве проводится по единственному пути от корня к искомому узлу (т.к. он проходит по единственному пути от корня к искомому узлу). Вып-ся те же операции как и с обычным деревом. Легче выпся оп-ии с поиском мин и макс ключа.

Основные операции: 1.Создать пустое БД 2.Включить узел в БД 3.Удалить узел из БД 4.Опр-ть пусто ли БД 5.Вып-ть поиск данных в БД

6.Обработать данные в узлах БД

7.Сохр-ть и восст-ть БД

8.Уничтожить БД.

Поиск Эл-та в 2-ном дереве:

Tree_Search(T,K).

1.i f (t=nil) or (k=T^.key) then return T

2.i f k<T^.key then return Tree_Search(T^.left,k)

3. else return Tree_Search(T^.right,k)

Iterative_Tree_Search(T,K).

1.while (T<>nil) and (k<>T^.key) do 2.if k<T^.key then TT^.left

3.else TT^.right; returnT.

Min and Max.

Мин. Ключ в дереве поиска можно найти по УК-лю left от корня, пока зн-е очередного УК-ля не станет = 0.

Tree_Minimum(T).

1.while T^.left<>nil do TT^.left 2.return T.

Для макс. Нужно заменить в алг-ме Tree_Minimum(T) left на right.

Добавление и удаление эл-та.

Эти операции сохр-ют сво-во упорядоченности. Добавление: алг-м вставляет нов. Эл-т в соответствии с бин. поиском.

Tree_Insert(T,z).

1.if T=nil then new(T)

2.T^.keyz T^.leftnil T^.rightnil 3.else if z<T^.key then Tree_Insert(T^.left,z) 4.else Tree_Insert(T^.right)

5.return T.

Затраты операций пропорциональны высоте дерева. Удаление: возможны 3 случая. 1.удаляемая вершина не имеет сыновей.

2.удаляемая вершина имеет 1-ого сына.

3.вершина имеет 2-х сыновей.

Delete(T,k).

1.if T<> nil then if k<T^.key then Delete(T^.left,k) 2.else if k>T^.key then Delete(T^.right,k)

3.else если узел не имеет сыновей:

4.if (T^left=nil) and (T^.right=nil) then q:=T;T:=nil; Dispose(q). 5.else если узел имеет сыновей:

6.else if (T^.left=nil) then q:=T; T:=T^.right; Dispose(q). 7.else (if T^.right=nil) then q:=T; T:=T^.left; Dispose(q).

8.else if (T^.left<> nil)and(T^.right<> nil) then q:=T; T:=T^.left; Dispose(q).

24. Добавление элемента в двоичном дереве поиска.

Алгоритм InsertTree (T, k)

//k – добавляемое значение

1.if T=nil

2.New (T)

3.T^.key←k

4.T^.left←nil

5.T^.right←nil

6.else

7.if (k<T^.key) then InsertTree (T^.left, k)

8.else InsrtTree (T^.right, k)

9.end if

10.end if

11.return T

Рассмотрим, например, вставку узла 8 в дерево BinSTree_1. Начав с корневого узла 25, определяем, что узел 8 должен быть в левом поддереве узла 25 (8<25). В узле 10 определяем, что место узла 8 должно быть в левом поддереве узла 10, которое в данный момент пусто. Узел 8 вставляется в дерево в качестве левого сына узла 10.

25. Удаление элемента в двоичном дереве поиска.

Удаление узла

Дано: дерево Т с корнем n и ключом K.

Задача: удалить из дерева Т узел с ключом K (если такой есть). Алгоритм:

Если дерево T пусто, остановиться

Иначе сравнить K с ключом X корневого узла n.

o Если K>X, рекурсивно удалить K из правого поддерева Т. o Если K<X, рекурсивно удалить K из левого поддерева Т.

oЕсли K=X, то необходимо рассмотреть два случая.

Если одного из детей нет, то значения полей второго ребёнка m ставим вместо соответствующих значений корневого узла, затирая его старые значения, и освобождаем память, занимаемую узлом m.

Если оба потомка присутствуют, то

найдём узел m, являющийся самым левым узлом правого поддерева;

скопируем значения полей (ключ, значение) узла m в соответствующие поля узла n.

у предка узла m заменим ссылку на узел m ссылкой на правого потомка узла m (который, в принципе, может быть равен ПУСТО).

освободим память, занимаемую узлом m (на него теперь никто не указывает, а его данные были перенесены в узел n).

Алгоритм Delete (T, k)

1.if T≠nil then

2.if k<T^.key then Delete (T^.left, k)

3.else if k>T^.key then Delete (T^.right, k)

4.else

5.if (T^.left=nil) and (T^.right=nil) then

6. p←T, T←nil, Dispose (p)

7.else if (T^.left=nil) then

8. p←T, T←T^.right, Dispose (p)

9.else if (T^.right=nil) then

10. p←T, T←T^.left, Dispose (p)

11.else Del (T^.right, p)

Del (T, p)

1.if T^.left=nil then

2.p^.key←T^.key

3.p1←T

4.T←T^.right

5.Dispose (p1)

6.return

7.else

8.Del (T^.left, p)

Вычислительная сложность.

Средний вариант – O(log n), худший случай – O(n).

26. Абстрактная таблица. Основные операции. Способ реализации.

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

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

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

1.По месту хранения

a.В оперативной памяти

b.Во внешней памяти

2.По отношению связей между элементами

a.Линейная

b.Нелинейная

3.По упорядоченности элементов

a.Упорядоченные

b.Неупорядоченные

Операции:

1.Включить в таблицу новый элемент.

2.Удалить из таблицы элемент, поисковый ключ которого совпадает с заданным элементом.

3.Извлечь из таблицы элемент, поисковый ключ которого совпадает с заданным элементом.

4.Обойти элементы таблицы в порядке следования их поисковых ключей.

5.Определить, пуста ли таблица.

6.Определить количество элементов в таблице. Организации таблицы делятся на линейные и нелинейные. Способ реализации:

1.Линейный

a.Неупорядоченный массив

b.Неупорядоченный связный список

c.Упорядоченный массив

d.Упорядоченный связный список

2.Нелинейный

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

b.Сбалансированное дерево поиска

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

d.Б-дерево

27.AVL-деревья. Свойства. Вращение. Теорема высоты.

Для каждого узла высота его поддеревьев различается не более чем на 1. Все операции аналогины операциям над БДП за исключением вставки и удаления узла, т.к.необходимо сохранение сбалансированности. Для хранения инфы о сбалансированности вводят дополнительное поле (Balance), указывающее какое поддерево длиннее( leftheavy, rightheavy, balanced).

TBalance=( leftheavy, rightheavy, balanced); TAVLTree=^AVLNode;

AVLNode=record

Key:integer;

Left, right: TAVLTree; Balance:TBalance; End;

Адельсон-Вельский-Ландис

AVL-дерево с n ключами имеет высоту H = O(logN)

Дерево поиска называется АВЛ – деревом, если для каждой его вершины высоты левого и правого поддеревьев отличаются не более чем на 1.

АВЛ - дерево

Высота AVL-деревьев оценивается сверху логарифмически в зависимости от числа вершин: h <= C log2 n . Высота лежит в диапазоне от log2(n + 1) до 1.44 log2(n + 2)

Повороты при балансировке

Пусть r – корень АВЛ-дерева, у которого имеется левое поддерево (ТL) и правое поддерево (TR). Если добавление новой вершины в левое поддерево приведет к увеличению его высоты на 1, то возможны три случая:

1)если hL = hR, то ТL и TR станут разной высоты, но баланс не будет нарушен;

2)если hL < hR, то ТL и TR станут равной высоты, т. е. баланс даже улучшится;

3)если hL > hR, то баланс нарушиться и дерево необходимо перестраивать.

Введём в каждую вершину дополнительный параметр Balance (показатель баланса), принимающий следующие значения: -1, если левое поддерево на единицу выше правого;0, если высоты обоих поддеревьев одинаковы;1, если правое поддерево на единицу выше левого.

Если в какой-либо вершине баланс высот нарушается, то необходимо так перестроить имеющееся дерево, чтобы восстановить баланс в каждой вершине. Для восстановления баланса будем использовать процедуры поворотов АВЛ-дерева.

1

LL - поворот

 

 

 

 

 

 

p

 

 

p

 

 

 

 

-1

-2

A

0

 

 

B

 

 

q

 

 

 

 

L

 

 

 

 

 

 

A 0 -1

 

LL

 

B

0

 

 

 

 

 

L

 

=>

 

 

 

 

 

 

 

 

 

 

 

T3

 

 

 

 

 

 

 

T1

 

 

T1

q := p→Left q→Balance := 0 p→Balance := 0 p→Left := q→Right q→Right := p

p := q

2

LR – поворот

 

L

q

A

0 +

1

R

r

B

T1 T2

T2

T2

T3

Алгоритм на псевдокоде

LL - поворот

p

 

 

 

 

 

p

 

C -

-

 

 

 

B

0

 

LR

 

 

 

 

1

2

 

 

 

 

 

 

 

=>

A

0

 

C

+

 

 

 

 

10

 

 

 

 

-

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

-

01

+

1

T4

 

 

 

 

T2

T3

T3

T1

T4

LR – поворот

Алгоритм на псевдокоде

LR - поворот

q := p→Left, r := q→Right

IF (r→Balance<0) p→Balance := +1 ELSE p→Balance := 0 FI IF (r→Balance>0) q→Balance := –1 ELSE q→Balance := 0 FI r→Balance := 0

p→Left := r→Right, q→Right := r→Left r→Left := q, r→Right := p, p := r

3

RR – поворот

 

 

 

 

q

 

 

 

 

 

 

 

p

 

 

 

RR

 

 

+

+

 

 

=>

B 0

 

 

 

 

 

A 1

2

 

 

 

 

 

R

 

 

 

 

 

 

 

 

0

q

 

A 0

 

 

 

 

 

 

 

B

1+R

 

 

 

T3

T1

T2

T3

T1

T2

 

 

RR – поворот

Алгоритм на псевдокоде

RR - поворот

q := p→Right q→Balance := 0 p→Balance := 0 p→ Right:= q→ Left q→ Left := p

p := q

4

RL – поворот

 

p

A

+

 

 

q

 

1

 

 

 

 

+

 

 

 

 

 

2

R

 

 

 

 

 

 

C

0

 

 

 

 

-

RL

 

 

L

 

 

 

 

 

 

 

 

1

=>

 

 

 

 

-

 

 

r

 

B

 

 

 

1

 

 

 

0 +

 

 

 

 

 

1

 

 

 

 

 

 

 

p

 

 

B

0

 

A

 

C

+

0

10

 

 

 

-

 

 

 

1

 

 

T1

 

 

T2

T3

 

T4

T1

T4

T2

T3

 

 

 

RL – поворот

 

 

Алгоритм на псевдокоде

 

 

RL - поворот

 

q := p→ Right, r := q→ Left

IF (r→Balance>0) p→Balance := -1 ELSE p→Balance := 0 FI IF (r→Balance<0) q→Balance := 1 ELSE q→Balance := 0 FI r→Balance := 0

p→ Right:= r→ Left, q→ Left:= r→ Right r→ Left := p, r→Right := q, p := r

28. Добавление вершины в AVL-дерево.

Добавление новой вершины в АВЛ-дерево происходит следующим образом. Вначале добавим новую вершину в дерево так же как в случайное дерево поиска (проход по пути поиска до нужного места). Затем, двигаясь назад по пути поиска от новой вершины к корню дерева, будем искать вершину, в которой нарушился баланс (т. е. высоты левого и правого поддеревьев стали отличаться более чем на 1). Если такая вершина найдена, то изменим структуру дерева для восстановления баланса с помощью процедур поворотов.

Алгоритм на псевдокоде

Добавление в АВЛ – дерево (D: данные; Var p: pVertex);

Обозначим Рост – логическая переменная, которая показывает выросло дерево или нет.

IF (p = NIL)

new(p), p→Data := D, p→Left := NIL, p→Right := NIL p→Balance := 0, Рост := ИСТИНА

ELSE

IF (p→Data > D)

Добавление в АВЛ – дерево (D, p→Left)

IF (Рост = ИСТИНА) {выросла левая ветвь}

IF (p→Balance > 0) p→Balance := 0, Рост := ЛОЖЬ

Соседние файлы в папке сиаод