- •1. Абстрагирование типов
- •1.1. Понятие типа данных
- •1.1.1. Простые типы
- •1.1.2. Абстрактные типы
- •2. Идентификация объектов
- •2.1. Именование
- •2.2. Указание
- •2.2.1. Организация адресного пространства оперативной
- •2.2.2. Понятие указателя
- •2.2.3. Действия над указателями
- •2.2.4. Связывание идентификатора объекта с его
- •3. Время жизни объекта. Классы памяти
- •3.1. Понятие “времени жизни” объекта
- •3.2. Классы памяти
- •3.2.1. Статическая память
- •3.2.2. Автоматическая память
- •3.2.3. Динамическая память
- •4. Динамические структуры данных
- •4.1. Метод вычисляемого и хранимого адреса.
- •4.2. Понятие динамической структуры данных
- •4.3. Линейные динамические структуры данных (списки)
- •4.3.1. Основные виды списков
- •4.4. Односвязные списки
- •4.4.1. Включение узла в начало списка
- •4.4.2. Создание списка из n узлов за счет добавления
- •4.4.3. Создание списка из n узлов за счет добавления
- •4.4.4. Исключение узла из начала списка
- •4.4.5. Перестановка указателя
- •4.4.6. Поиск в списке узла по заданному условию
- •4.4.7. Включение нового узла в список за тем узлом, на
- •4.4.8. Исключение из списка узла за тем узлом, на
- •4.4.9. Исключение из списка узла, на который предварительно
- •4.4.10. Разрушение списка
- •4.4.11. Программный модуль, реализующий операции
- •4.5. Односвязные циклические списки
- •4.6. Двусвязные списки
- •4.6.1. Включение нового узла в список за тем узлом, на
- •4.6.2. Исключение из списка узла, на который
- •4.7. Ортогональные списки (мультисписки)
- •4.8. Разнородные списки
- •4.9. Управление динамической памятью
- •4.9.1. Администратор кучи
- •4.9.2. Алгоритмы выделения участков памяти по запросу
- •4.9.3. Фрагментация
- •4.9.4. Накопление мусора
- •4.9.5. Висящие ссылки
- •5. Множественная интерпретация объектов
- •5.1. Совместимость типов. Приведение и преобразование типов
- •5.2. Методы совмещения типов
- •5.2.1. Запись с вариантной частью
- •5.2.2. Использование директивы absolute
- •5.2.3. Параметры без типа
- •5.2.4. Открытые массивы
- •5.2.5. Наложение масок с помощью указателей
- •6. Рекурсивные структуры данных
- •6.1. Итерация и рекурсия в программировании
- •6.1.1. Понятие рекурсии
- •6.1.2. Итеративная и рекурсивная схема организации
- •6.2. Задача о “ханойских башнях”
- •6.3. Виды рекурсивных структур данных
- •6.3.1. Арифметические выражения
- •6.3.2. Динамические линейные структуры данных: списки
- •6.3.3. Иерархические линейные структуры данных: наборы
- •6.4. Эффективность рекурсивных вычислений
- •7. ИерархическиеНелинейные структуры данных.Деревья
- •7.1. Деревья общего вида
- •7.2. Бинарные деревья
- •7.3. Представление бинарных деревьев
- •7.3.1. Представление бинарных деревьев на статической
- •7.3.2. Представление бинарных деревьев на
- •7.4. Алгоритмы обхода бинарных деревьев
- •7.5. Виды бинарных деревьев
- •7.5.1. Сбалансированные деревья
- •7.5.2. Дихотомические деревья (деревья поиска)
- •7.5.3. Деревья выражений
- •7.6. Программный модуль, реализующий операции
- •Список рекомендуемой литературы
7.5. Виды бинарных деревьев
7.5.1. Сбалансированные деревья
Дерево называется идеально сбалансированным, если число узлов в его правом и левом поддеревьях отличается не более чем на единицу(далее будем называть такие деревья сбалансированными). Сбалансированное дерево, состоящее изNузлов, имеет минимальную высоту среди всех бинарных деревьев. Высоту сбалансированного дерева можно определить по формуле:
.
Минимальная высота при заданном числе узлов достигается, если на всех уровнях, кроме последнего, размещается максимально возможное число узлов. Это происходит за счет равномерного размещения узлов поровну слева и справа от каждого узла.
Правило равномерного размещения для Nузлов (правило 1) формулируется с помощью рекурсии:
взять один узел в качестве корня;
по правилу 1построить левое поддерево с числом узловnl = N div 2;
по правилу 1построить правое поддерево с числом узловnr=N- nl - 1.
Структура сбалансированного дерева из Nузлов определяется количеством узлов (рис. 69).
N = 5
Рис. 69. Сбалансированное дерево
Процедура построения сбалансированного дерева из Nузлов:
Type |
| |||
PTree = ^ Tree; |
{ тип – указатель на узел сбалансированного дерева } | |||
Tree = record |
{ тип – элемент хранения узла сбалансированного дерева } | |||
info: char; |
| |||
left, right: PTree |
| |||
end; |
| |||
|
| |||
Procedure Balance( var root: PTree; |
{ root – указатель на корень дерева, } | |||
n: byte ); |
{ n – количество узлов в дереве } | |||
begin |
| |||
if n = 0 then root:=nil |
{ если n = 0, построить пустое дерево } | |||
else begin |
| |||
new( root ); |
{ создать корень дерева } | |||
write( ‘Значение инф. поля‘, i, ‘-го узла дерева = ‘ ); |
| |||
readln( root^.info ); |
{ заполнить информационное поле корня } | |||
Balance( root^.left, n div 2); |
{ построить левое поддерево} | |||
(*1*) Balance( root^.right, n – n div 2 – 1) |
{ построить правое поддерево } | |||
(*2*) end |
| |||
(*3*)end; |
|
Таблица 5. Трасса состояний стека при работе процедуры построения сбалансированного дерева
Номер уровня рекур- сии |
Знач-я входного парам-ра n |
Стек |
Знач-я вых. пар-ра |
Выход | |||
Исходное состояние (n,root, (.) возврата) |
Конечное состояние (n,root, (.) возврата) |
root |
root^.left |
root^.right |
из уровня | ||
1 |
3 |
(-, -, -) |
(3,^A, *1*) |
^A |
- |
- |
- |
2 |
1 |
(3,^A, *1*) |
(1, ^B, *1*) (3, ^A, *1*) |
^B |
- |
- |
- |
3 |
0 |
(1, ^B, *1*) (3, ^A, *1*) |
(0,NIL,*1*) (1, ^B, *1*) (3, ^A, *1*) |
NIL |
- |
- |
- |
0 |
(0,NIL,*1*) (1, ^B, *1*) (3, ^A, *1*) |
(1, ^B, *1*) (3, ^A, *1*) |
NIL |
- |
- |
(* 3 *) | |
2 |
1 |
(1, ^B, *1*) (3, ^A, *1*) |
(1, ^B, *2*) (3, ^A, *1*) |
^B |
NIL |
- |
- |
3 |
0 |
(1, ^B, *2*) (3, ^A, *1*) |
(0,NIL,*2*) (1, ^B, *2*) (3, ^A, *1*) |
NIL |
- |
- |
- |
0 |
(0,NIL,*2*) (1, ^B, *2*) (3, ^A, *1*) |
(1, ^B, *2*) (3, ^A, *1*) |
NIL |
- |
- |
(* 3 *) | |
2 |
1 |
(1, ^B, *2*) (3, ^A, *1*) |
(3, ^A, *1*) |
^B |
NIL |
NIL |
(* 3 *) |
1 |
3 |
(3, ^A, *1*) |
(3, ^A, *2*) |
^A |
^B |
- |
- |
2 |
1 |
(3,^A, *2*) |
(1, ^С, *1*) (3, ^A, *2*) |
^C |
- |
- |
- |
3 |
0 |
(1, ^С, *1*) (3, ^A, *2*) |
(0,NIL,*1*) (1, ^С, *1*) (3, ^A, *2*) |
NIL |
- |
- |
- |
0 |
(0,NIL,*1*) (1, ^С, *1*) (3, ^A, *2*) |
(1, ^С, *1*) (3, ^A, *2*) |
NIL |
- |
- |
(* 3 *) | |
2 |
1 |
(1, ^С, *1*) (3, ^A, *2*) |
(1, ^С, *2*) (3, ^A, *2*) |
^C |
NIL |
|
|
3 |
0 |
(1, ^С, *2*) (3, ^A, *2*) |
(0,NIL,*2*) (1, ^С, *2*) (3, ^A, *2*) |
NIL |
- |
- |
- |
0 |
(0,NIL,*2*) (1, ^С, *2*) (3, ^A, *2*) |
(1, ^С, *2*) (3, ^A, *2*) |
NIL |
- |
- |
(* 3 *) | |
2 |
1 |
(1, ^С, *2*) (3, ^A, *2*) |
(3, ^A, *2*) |
^C |
NIL |
NIL |
(* 3 *) |
1 |
3 |
(3, ^A, *2*) |
(-, -, -) |
^A |
^B |
^C |
(* 3 *) |
Для иллюстрации работы этой процедуры и построения трассы состояний стека будем использовать следующие обозначения:
^A– адрес узла дерева, в информационном поле которого хранится символ“A”;
(* 1 *) и (* 2 *) – адреса возврата из уровня рекурсивной процедуры, номер которой на 1 больше текущего;
(* 3 *) – адрес возврата из рекурсивной процедуры текущего уровня.
Трасса построения сбалансированного дерева, содержащего 3 узла, приведена в таблице 5.
Сбалансированное дерево, как и дерево любого другого вида, можно построить в процессе нисходящего обхода. Особенность работы процедуры построения дерева заключается в том, что сначала создаются корневые узлы, адреса которых запоминаются в стеке. Установить ссылки из корневых узлов дерева на их поддеревья можно только после того, как эти поддеревья будут созданы и адреса их корневых узлов возвращены в точки вызова. Точками вызова являются поля ссылок на левое поддерево (root^.left) или правое поддерево(root^.right) корневого узла (в таблице установка этих ссылочных связей показана стрелками). Подобная возможность обеспечивается за счет того, чтоуказатель на корень дерева является параметром-переменной процедуры.