- •Введение
- •Основные понятия и определения
- •Типы данных
- •1.1.1. Понятие типа данных
- •1.2.2. Внутреннее представление базовых типов в оперативной памяти
- •1.2.2. Внутреннее представление структурированных типов данных
- •1.2.3. Статическое и динамическое выделение памяти
- •Абстрактные типы данных (атд)
- •Понятие атд
- •1.2.2. Спецификация и реализация атд
- •Структуры данных
- •1.3.1. Понятие структуры данных
- •1.3.2. Структуры хранения — непрерывная и ссылочная
- •1.4.3. Классификация структур данных
- •Алгоритмы
- •1.4.1. Понятие алгоритма
- •1.4.2. Способы записи алгоритмов.
- •1.4.3. Введение в анализ алгоритмов Вычислительные модели
- •Задача анализа алгоритмов
- •Время работы алгоритма
- •Время выполнения в худшем и среднем случае
- •1.4.3. Введение в рекурсию
- •Первые примеры
- •1.5.1. Введение в «длинную» арифметику
- •1.5.2. Рекурсия
- •1.5.3. Поразрядные операции. Реализация атд «Множество»
- •2. Линейные структуры данных
- •2.1. Атд "Стек", "Очередь", "Дек"
- •2.2. Реализация стеков
- •2.2.1. Непрерывная реализация стека с помощью массива
- •2.2.2. Ссылочная реализация стека в динамической памяти
- •2.2.3. Примеры программ с использованием стеков
- •2.3. Реализация очередей
- •2.3.2. Непрерывная реализация очереди с помощью массива
- •2.3.2. Ссылочная реализация очереди в динамической памяти
- •2.3.3. Ссылочная реализация очереди с помощью циклического списка
- •2.3.4. Очереди с приоритетами
- •2.3.5. Пример программы с использованием очереди
- •2.4. Списки как абстрактные типы данных
- •2.4.1. Модель списка с выделенным текущим элементом
- •2.4.2. Однонаправленный список (список л1)
- •2.4.3. Двунаправленный список (список л2)
- •2.4.4. Циклический (кольцевой) список
- •2.5. Реализация списков с выделенным текущим элементом
- •2.5.1. Однонаправленные списки Ссылочная реализация в динамической памяти на основе указателей
- •2.5.2. Двусвязные списки
- •2.5.3. Кольцевые списки
- •2.5.4. Примеры программ, использующих списки Очередь с приоритетами на основе линейного списка
- •Задача Иосифа (удаление из кольцевого списка)
- •2.6. Рекурсивная обработка линейных списков
- •2.6.1. Модель списка при рекурсивном подходе
- •2.6.2. Реализация линейного списка при рекурсивном подходе
- •3. Иерархические структуры данных
- •3.1. Иерархические списки
- •3.1.1 Иерархические списки как атд
- •3.1.2. Реализация иерархических списков
- •3.2. Деревья и леса
- •3.2.1. Определения
- •3.2. Способы представления деревьев
- •3.2.3. Терминология деревьев
- •3.2.4. Упорядоченные деревья и леса. Связь с иерархическими списками
- •3.3. Бинарные деревья
- •3.3.1. Определение. Представления бинарных деревьев
- •3.3.2. Математические свойства бинарных деревьев
- •3.4. Соответствие между упорядоченным лесом и бинарным деревом
- •3.5. Бинарные деревья как атд
- •3.6. Ссылочная реализация бинарных деревьев
- •3.6.1. Ссылочная реализация бинарного дерева на основе указателей
- •3.6.2. Ссылочная реализация на основе массива
- •3.6.3. Пример — построение дерева турнира
- •3.7. Обходы бинарных деревьев и леса
- •3.7.1. Понятие обхода. Виды обходов
- •3.7.2. Рекурсивные функции обхода бинарных деревьев
- •3.7.3. Нерекурсивные функции обхода бинарных деревьев
- •3.7.4. Обходы леса
- •3.7.5. Прошитые деревья
- •3.8. Применения деревьев
- •3.8.1. Дерево-формула
- •3.8.2. Задача сжатия информации. Коды Хаффмана
- •4. Сортировка и родственные задачи
- •4.1. Общие сведения
- •4.1.1. Постановка задачи
- •4.1.2. Характеристики и классификация алгоритмов сортировки
- •4.2. Простые методы сортировки
- •4.2.1. Сортировка выбором
- •4.2.2. Сортировка алгоритмом пузырька
- •4.2.3.Сортировка простыми вставками.
- •4.3. Быстрые способы сортировки, основанные на сравнении
- •4.3.1. Сортировка упорядоченным бинарным деревом
- •Анализ алгоритма сортировки бинарным деревом поиска
- •4.3.2. Пирамидальная сортировка
- •Первая фаза сортировки пирамидой
- •Вторая фаза сортировки пирамидой
- •Анализ алгоритма сортировки пирамидой
- •Реализация очереди с приоритетами на базе пирамиды
- •4.3.2. Сортировка слиянием
- •Анализ алгоритма сортировки слиянием
- •4.3.3. Быстрая сортировка Хоара
- •Анализ алгоритма быстрой сортировки
- •4.3.4. Сортировка Шелла
- •4.3.5. Нижняя оценка для алгоритмов сортировки, основанных на сравнениях
- •4.4. Сортировка за линейное время
- •4.4.1. Сортировка подсчетом
- •4.4.2. Распределяющая сортировка от младшего разряда к старшему
- •4.4.3. Распределяющая сортировка от старшего разряда к младшему
- •5. Структуры и алгоритмы для поиска данных
- •5.1. Общие сведения
- •5.1.1. Постановка задачи поиска
- •5.1.2. Структуры для поддержки поиска
- •5.1.3. Соглашения по программному интерфейсу
- •5.2. Последовательный (линейный) поиск
- •5.3. Бинарный поиск в упорядоченном массиве
- •5.4. Бинарные деревья поиска
- •5.4.1. Анализ алгоритмов поиска, вставки и удаления Поиск
- •Вставка
- •Удаление
- •5.4.3. Реализация бинарного дерева поиска
- •5.5. Сбалансированные деревья
- •Определение и свойства авл-деревьев
- •Вращения
- •Алгоритмы вставки и удаления
- •Реализация рекурсивного алгоритма вставки в авл-дерево
- •5.5.2. Сильноветвящиеся деревья
- •Бинарные представления сильноветвящихся деревьев
- •5.5.3. Рандомизированные деревья поиска
- •5.6. Структуры данных, основанные на хеш-таблицах
- •5.6.2. Выбор хеш-функций и оценка их эффективности
- •Модульное хеширование (метод деления)
- •Мультипликативный метод
- •Метод середины квадрата
- •5.6.2. Метод цепочек
- •5.6.3. Хеширование с открытой адресацией
- •5.6.4. Пример решения задачи поиска с использованием хеш-таблицы
3.2. Деревья и леса
Другой подход к представлению иерархических структур представляют собой деревья и леса. Это наиболее распространенные структуры, которые используются как базовые большим количеством алгоритмов обработки данных, в связи с чем заслуживают самого пристального внимания.
Аналогично другим структурам данных, таким как линейные и иерархические списки, можно ввести понятие АТД «Дерево» или «Лес», представив формальную функциональную спецификацию. Однако такая спецификация будет мало полезна, поскольку имеется огромное количество алгоритмов, использующих деревья (леса), причем часто это деревья специального вида со своими специфическими функциями. Для большинства таких алгоритмов применение универсального типа «Дерево» или «Лес» приведет к неэффективной реализации. Поэтому деревья и леса обычно используются как составная часть более сложных АТД (очереди с приоритетами, множества, словари и т. д.). Некоторые из этих АТД будут рассмотрены далее.
В данной главе сосредоточим максимум внимания на деревьях и лесах как структурах данных. Для этого сначала рассмотрим их как абстрактные математические объекты, а затем перейдем к различным формам представления в памяти и соответствующим алгоритмам.
3.2.1. Определения
Когда речь идет об иерархических структурах, то под термином «дерево» обычно понимают дерево с корнем (корневое дерево). Однако заметим, что корневое дерево — это частный случай более общего определения дерева, называемого иначе свободным деревом. Свободные деревья, в свою очередь, являются частным случаем графов и определяются в терминах теории графов. Они будут рассмотрены позже.В данной главе будем рассматривать только корневые деревья, называя их просто деревьями.
Особый вид иерархических структур — бинарные деревья — будут рассмотрены отдельно.
Формально дерево можно рекурсивно определить следующим образом[8].
Дерево (tree) — конечное множество T одного или более узлов (nodes) со следующими свойствами:
Существует один выделенный узел, называемый корнем (root) этого дерева T. Дерево может состоять и из одного корня.
Остальные узлы (если они есть) распределены среди k непересекающихся множеств T1, Т2, ..., Tk, и каждое их этих множеств, в свою очередь, является деревом. Деревья T1, Т2, ..., Tk называются поддеревьями (subtrees) этого корня.
Из этого определения следует, что каждый узел дерева является корнем некоторого другого дерева (поддерева).
Совокупность нескольких непересекающихся деревьев называется лесом (forest —иногда переводится как бор). Например, все потомки одного узла дерева образуют лес. Лес всегда можно преобразовать в дерево, добавив один единственный корневой элемент и связав его с корнями всех деревьев, из которых состоит лес. Поэтому лес и дерево — это два неразрывно сязанных понятия. Для того, чтобы подчеркнуть общность этих понятий, лес из n деревьев иногда называют деревом с n-кратным корнем.
3.2. Способы представления деревьев
Существует множество способов представления деревьев, одни из них используют двухмерные рисунки для наглядного отображения отношений иерархии, в других эти отношения удается отобразить и при помощи одномерного представления. Остановимся на наиболее часто используемых способах.
Рис.3.2. Графическое изображение дерева
Традиционно деревья изображают графически, располагая корень вверху (т. е. дерево растет вниз), как показано на рис. 3.2. Очевидно, такое представление связано с тем, что человеку привычнее рисовать и читать рисунок сверху вниз. Узлы дерева обычно изображают с помощью окружностей, соединяя каждый узел с его сыновьями линиями (связями). Связи обычно изображают без стрелки на конце.
Другим представлением может быть так называемый уступчатый список. На рис.3.3,а,б так представлено дерево из рис.3.2.
a ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ а
b ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ b
i ▓▓▓▓▓▓▓▓▓▓ i
j ▓▓▓▓▓▓▓▓▓▓ j
c ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ c
h ▓▓▓▓▓▓▓▓▓▓ h
d ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ d
e ▓▓▓▓▓▓▓▓▓▓ e
f ▓▓▓▓▓▓▓▓▓▓ f
k ▓▓▓▓▓▓▓ k
g ▓▓▓▓▓▓▓▓▓▓ g
а ) б)
Рис.3.3. Представление дерева: а – в виде уступчатого списка; б – в виде “упрощенного”уступчатого списка
Здесь двухмерность рисунка поддерживается посредством отступов.
Более компактными являются одномерные способы изображения деревьев. Например, от списка с отступами можно легко перейти к десятичной системе обозначений Дьюи, которая используется в библиографии. Например, для нашего дерева она будет выглядеть так:
1.a, 1.1.b, 1.1.1.i, 1.1.2.j, 1.2.c, 1.2.1.h,
1.3.d, 1.3.1.e, 1.3.2.f, 1.3.2.1.k, 1.3.3.g
Другой вид одномерного представления дерева - это так называемая скобочная запись, в которой отношения иерархии представляются с помощью вложенности скобок. Один из возможных вариантов скобочной записи [8] для дерева (рис.3.2) выглядит так:
(a (b (i) (j) ) (c (h) ) (d (e) (f (k) ) (g)))
Можно немного сократить количество скобок:
a ( b ( i, j ), c ( h ), d ( e, f ( k ), g ) )
Такой способ называется левым скобочным представлением дерева, поскольку корень каждого поддерева расположен слева от скобки, открывающей список его поддеревьев. Возможны и другие способы перечисления порядка узлов, например, правое скобочное представление, в котором корень расположен справа. Заметим, что различные формы скобочного представления связаны с понятием обхода дерева, который будет подробно рассмотрен ниже.
В заключение упомянем еще один очень компактный способ представления деревьев, который основан на очень важном свойстве иерархической структуры. На рис. 3.2 хорошо видно, что дерево разветвляется от корня к листьям, т. е. каждый узел (кроме корня) имеет только один связанный с ним родительский (вышестоящий) узел. Из этого следует, что возможно такое простое представление дерева, в котором для каждого узла указана одна единственная ссылка на его родителя (для корня — пустая ссылка).
Например, представим дерево из рис. 3.2. в виде таблицы, содержащей для каждого узла обозначение его родителя. Для экономии места расположим таблицу горизонтально, хотя логичнее представить дерево в виде таблицы из двух столбцов— «узел»-«его родитель».
Таблица 3.2.
Предствление дерева из рис. 3.2 с помощью ссылок на родителей
Узел |
a |
B |
c |
d |
i |
j |
h |
e |
F |
g |
k |
Родитель |
nil |
A |
a |
a |
b |
b |
c |
d |
D |
d |
f |
Подобное представление фактически не используется для наглядного представления деревьев, т. к. сильно проигрывает в этом плане всем расмотренным выше способам представления. В большинстве алгоритмов, использующих деревья, такая структура также не приведет к эффективной реализации, поскольку чаще необходимо движение по дереву от корня к листьям, чем наоборот. Однако есть область, где такой способ представления иерархической информации является основным — это реляционные (табличные) базы данных. В связи с этим обратим на него внимание.
Еще один способ представления деревьев с помощью указания левого сына и правого брата каждого узла также не обладает достаточной наглядностью, но очень удобен для эффективной реализации. Этот способ будет подробно рассмотрен ниже (см. разд. 3.4).