- •Оглавление
- •Введение
- •Раздел 1. Основные структуры данных
- •Тема 1. Введение в структуры данных. Динамическое распределение памяти
- •Классификация структур данных
- •1.2. Переменные-указатели и динамическое распределение памяти.
- •Например, указатели на простейшие базовые типы вводятся следующим образом:
- •Var pStr1,pStr2 :TpString; {переменные-указатели на строки}
- •1.3. Дополнительные вопросы использования переменных-указателей
- •1.4. Контрольные вопросы по теме
- •Тема 2. Структуры данных “стек” и “очередь”
- •2.1. Что такое стек и очередь?
- •2.2. Статическая реализация стека
- •2.3. Динамическая реализация стека
- •{Ссылочный тип для адресации элементов стека}
- •2.4. Статическая реализация очереди
- •2.5. Динамическая реализация очереди
- •{Ссылочный тип для адресации элементов очереди}
- •2.6. Практические задания
- •2.7. Контрольные вопросы по теме
- •Тема 3. Основы реализации списковых структур
- •3.1. Структуры данных типа “линейный список”
- •3.2. Первый способ статической реализации списка.
- •3.3. Второй способ статической реализации списка.
- •3.4. Управление памятью при статической реализации списков
- •3.5. Динамическая реализация линейных списков
- •3.6. Практические задания
- •3.7. Контрольные вопросы по теме
- •Тема 4. Усложненные списковые структуры
- •4.1. Двунаправленные линейные списки
- •4.2. Комбинированные структуры данных: массивы и списки указателей
- •4.3. Комбинированные структуры данных: массивы и списки списков
- •4.4. Практические задания.
- •4.5. Контрольные вопросы по теме
- •Тема 5. Основные понятия о древовидных структурах
- •5.1. Основные определения
- •5.2. Двоичные деревья
- •5.3. Идеально сбалансированные деревья
- •5.4. Практические здания
- •5.5. Контрольные вопросы по теме
- •Тема 6. Реализация поисковых деревьев
- •Двоичные деревья поиска.
- •6.2. Добавление вершины в дерево поиска
- •6.3. Удаление вершины из дерева поиска
- •6.4. Практические задания
- •Контрольные вопросы по теме
- •Тема 7. Дополнительные вопросы обработки деревьев. Графы.
- •Проблемы использования деревьев поиска
- •7.2. Двоичные деревья с дополнительными указателями
- •7.3. Деревья общего вида (не двоичные).
- •Представление графов
- •Практические задания
- •Контрольные вопросы по теме
- •Раздел 2. Алгоритмы сортировки и поиска
- •Тема 1. Классификация методов. Простейшие методы сортировки
- •1.1. Задача оценки и выбора алгоритмов
- •1.2. Классификация задач сортировки и поиска
- •1.3. Простейшие методы сортировки: метод обмена
- •1.4. Простейшие методы сортировки: метод вставок
- •1.5. Простейшие методы сортировки: метод выбора
- •1.6. Практическое задание
- •1.7. Контрольные вопросы по теме
- •Тема 2. Улучшенные методы сортировки массивов
- •2.1. Метод Шелла
- •2.2. Метод быстрой сортировки
- •2.3. Пирамидальная сортировка
- •2.4. Практическое задание
- •2.5. Контрольные вопросы по теме
- •Тема 3. Специальные методы сортировки
- •3.1. Простейшая карманная сортировка.
- •3.2. Карманная сортировка для случая повторяющихся ключей
- •3.3. Поразрядная сортировка
- •3.4. Практическое задание
- •3.5. Контрольные вопросы по теме
- •Тема 4. Поиск с использованием хеш-функций
- •4.1. Основные понятия
- •4.2. Разрешение конфликтов: открытое хеширование
- •4.3. Разрешение конфликтов: внутреннее хеширование
- •4.4. Практические задания
- •4.5. Контрольные вопросы по теме
- •Тема 5. Внешний поиск и внешняя сортировка
- •5.1. Особенности обработки больших наборов данных
- •5.2. Организация внешнего поиска с помощью б-деревьев.
- •5.4. Поиск элемента в б-дереве.
- •5.5. Добавление вершины в б-дерево
- •5.6. Удаление вершины из б-дерева
- •5.7. Внешняя сортировка
- •5.8. Практические задания
- •5.9. Контрольные вопросы по теме
- •Основные термины и понятия
- •Литература
5.3. Идеально сбалансированные деревья
В заключение данной темы рассмотрим один частный случай ДД – так называемое идеально сбалансированное дерево (ИСД). Как будет отмечено в дальнейшем, эффективное использование деревьев на практике часто требует управления ростом дерева для устранения крайних случаев, когда дерево вырождается в линейный список и тем самым теряет всю свою привлекательность (с вычислительной точки зрения, разумеется).
В этом смысле ИСД полностью оправдывает свое название, поскольку вершины в нем распределяются наиболее равномерно и тем самым ИСД имеет минимально возможную высоту. Более точно, ДД называется идеально сбалансированным, если длякаждойвершины число вершин в левом и правом ее поддеревьях отличаются не более чем на единицу. Обратим внимание, что данное условие должно выполняться для всех вершин дерева!
|
|
|
Это дерево - ИСД |
Это – не ИСД (нарушение условия для корня!) |
Это тоже не ИСД (совсем плохо, почти список!) |
ИСД легко строится, если заранее известно количество вершин Nв этом дереве. В этом случае ИСД можно построить с помощью следующего рекурсивного алгоритма:
взять первую по порядку вершину в качестве корневой
найти количество вершин в левых и правых поддеревьях:
Nl = N div 2;
Nr = N – Nl – 1;
построить левое поддерево с Nlвершинами точно таким же образом (пока не получимNl= 0)
построить правое поддерево с Nrвершинами точно таким же образом (пока не получимNr= 0)
Естественно, что реализация рекурсивного алгоритма наиболее просто выполняется в виде рекурсивной подпрограммы. При этом между этой процедурой и процедурами обхода есть одно принципиальное различие: процедуры обхода лишь используютсуществующую структуру дерева, не изменяя ее, и поэтому их формальные параметры являются лишь входными, тогда как процедура построения ИСД должнаСОЗДАВАТЬвершины и каждый раз возвращать в вызвавшую ее подпрограмму адрес очередной созданной вершины. Поэтому формальный параметр ссылочного типа должен быть объявлен какпараметр-переменная. Кроме того, второй формальный параметр-значение принимает число вершин в текущем строящемся поддереве.
procedure AddNodes ( var pСurrent : Tp; aN : integer);
var pTemp : Tp;
Nl, Nr : integer;
begin
ifaN= 0then{ вершин для размещения нет }
pCurrent:=nil{ формируем пустую ссылку }
else
begin
Nl:=aNdiv2; {сколько вершин будет слева?}
Nr:=aN-Nl- 1; {сколько вершин будет справа?}
New(pTemp); {создаем корень поддерева}
AddNodes(pTemp^.left,Nl); {уходим на создание левого поддерева}
AddNodes(pTemp^.right,Nr);{уходим на создание правого поддерева}
pCurrent:=pTemp; {возвращаем адрес созданного корня}
end
end;
Запуск процесса построения как обычно выполняется из главной программы с помощью вызова AddNodes(pRoot,N). В этом вызове фактический параметрNобязательно должен иметь конкретное значение, например – заданное пользователем количество вершин в строящемся ИСД. Однако, первый фактический параметрpRoot, являясь выходным, получит свое значение лишьпосле отработкивсех рекурсивных вызовов, при возврате в главную программу.
Для понимания работы приведенной процедуры целесообразно вручную расписать шаги ее работы для простейшего дерева из трех вершин. Пусть элементами дерева являются символы A,B,C. В результате работы мы должны получить:pRoot->A,A.left->B,A.right->C,B.left->nil,B.right->nil,C.left->nil,C.right->nil.
Тогда схема построения ИСД будет:
Главная программа: вызов AddNodes(pRoot, 3)
П/п 1: Nl= 1,Nr= 1, создание вершиныA, вызовAddNodes(A.left, 1)
П/п 1-2: сохранение в стеке значений Nl= 1,Nr= 1, адресаA
П/п 1-2: Nl= 0,Nr= 0, создание вершиныB, вызов
AddNodes(B.left, 0)
П/п 1-2-3: сохранение в стеке значений Nl= 0,Nr= 0, адресаB
П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2
П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр B.left = nil
П/п 1-2: вызов AddNodes (B.right, 0)
П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса B
П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2
П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр B.right = nil
П/п 1-2: pCurrent = адрес B, возврат к 1
П/п 1: восстановление Nl = 1, Nr = 1, вых. параметр A.left=адрес B
П/п 1: вызов AddNodes (A.right, 1)
П/п 1-2: сохранение в стеке значений Nl = 1, Nr = 1, адреса A
П/п 1-2: Nl = 0, Nr = 0, создание вершины C, вызов
AddNodes(C.left, 0)
П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса C
П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2
П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр C.left = nil
П/п 1-2: вызов AddNodes (C.right, 0)
П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса C
П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2
П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр C.right = nil
П/п 1-2: pCurrent = адрес C, возврат к 1
П/п 1: восстановление Nl = 1, Nr = 1, вых. параметр A.right=адрес C
П/п 1: pCurrent = адрес A, возврат в главную программу
Главная программа: установка выходного параметра pRoot = адрес A