- •Введение
- •Основные понятия и определения
- •Типы данных
- •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. Пример решения задачи поиска с использованием хеш-таблицы
5.5.2. Сильноветвящиеся деревья
Еще одна группа сбалансированных деревьев поиска, которая использует другой способ поддержки сбалансированности, — сильноветвящиеся деревья. Напомним, что узлы сильноветвящихся деревьев могут иметь более двух сыновей, т. е. их нельзя отнести к бинарным деревьям. Однако принцип упорядоченности для сильноветвящихся деревьев аналогичен принципу, по которому строятся бинарные деревья поиска. А для того, чтобы обеспечить ветвление более чем по двум направлениям, узлам разрешено иметь более одного ключа.
Допустим, узел дерева имеет два ключа — 10 и 20. Такой узел может иметь максимум три сына — первый (крайний левый) должен иметь ключи, меньшие 10, второй — от 10 до 20 (10 входит в этот диапазон), третий (крайний правый) — более или равно 20.
На практике используются различные виды сильноветвящихся деревьев. В качестве структур для поиска в оперативной памяти применяются 2-3 деревья (их узлы имеют двух или трех сыновей) и 2-3-4 деревья (узлы могут иметь еще и четырех сыновей). Для поиска во внешней памяти наиболее подходят B-деревья, которые могут ветвиться еще сильней (B — balansed). B-деревья в настоящее время являются основными структурами для поиска в базах данных.
Различают две разновидности сильноветвящихся деревьев поиска. В первом случае все данные, в которых ведется поиск, располагаются только в листьях, а внутренние узлы содержат ключи-разделители, которые задают диапазоны ключей в поддеревьях и служат для выбора правильного направления движения по пути поиска. Во втором случае данные распределены по всем узлам.
В качестве примера рассмотрим структуру 2-3 дерева, в котором вся информация размещается в листьях [3]. Будем считать этот материал введением в сильноветвящиеся деревья поиска.
2-3 деревья
Пример 2-3 дерева изображен на рис. 5.14. Поскольку внутренние узлы могут содержать два ключа, на рисунке они изображены в виде прямоугольников, а листья, содержащие только один ключ, изображены, как обычно, в виде окружностей. Свойства 2-3 деревьев можно определить так.
Каждый лист содержит ключ и связанные с ним данные (на рисунке показаны только ключи). В графическом представлении 2-3 дерева ключи в листьях упорядочены слева направо.
Каждый внутренний узел содержит один или два ключа. Узел, содержащий один ключ, имеет двух сыновей, узел, содержащий два ключа, имеет трех сыновей.
Первым ключом внутреннего узла является ключ наименьшего из потомков второго сына (второй сын всегда есть, а его наименьший потомок может использоваться в качестве разделителя между первым и вторым поддеревом). Вторым ключом может являться ключ наименьшего потомка третьего сына, если третий сын есть. Тогда его наименьший потомок задает границу между вторым и третьим поддеревом.
Все листья располагаются на одном уровне. Исходя из этого условия, решается вопрос, сколько сыновей должен иметь каждый из внутренних узлов — два или три.
Добавим, что пустое дерево и дерево с одним корнем также являются 2-3 деревьями.
Рис.5.14. Пример 2-3 дерева
2-3-дерево с k уровнями может иметь от 2k-1 до 3k-1 листьев. Если в дереве n элементов, то дерево будет иметь от 1+log3n до 1+log2n уровней. Таким образом, длины всех путей имеют порядок O(log2n).
Рассмотрим выполнение основных операций над 2-3 деревьями.
Поиск элемента в 2-3- дереве
Поиск выполняется аналогично поиску в бинарном дереве с той разницей, что для тех узлов, которые имеют два ключа, разветвление возможно по трем направлениям — к первому (левому), второму или третьему сыну, в зависимости от значения ключа. Например, для того, чтобы найти элемент с ключом 16 на рис.5.14, переходим от корня к его третьему сыну, а из этого узла переходим к первому сыну (он уже является листом и его ключ равен 16, поэтому поиск закончен успешно). При попытке найти ключ 18, будем двигаться по тому же самому пути, но, дойдя до листа, не обнаружим там значение 18, и поиск закончится промахом.
Вставка элемента в 2-3-дерево
Как обычно, сначала находим место, куда необходимо вставить новый элемент, двигаясь по пути поиска, как описано выше. Пусть мы уже спустились на уровень, непосредственно предшествующий листьям, и стоим в узле node. Далее возможны два варианта действий.
Если узел node имеет два сына, то делаем новый элемент третьим, помещая его в правильном порядке среди других сыновей, и изменяем значения ключей в родителе node. Например, на рис.5.15 показано добавление элемента с ключом 18 , к дереву ни рис.5.14. Больше никаких преобразований структуры не требуется, поскольку сбалансированность дерева не нарушена. Это простой случай.
Рис. 5.15 Вставка элемента 18 в 2-3 дерево.
Второй случай сложнее. Если узел node уже имеет трёх сыновей, то новый добавляемый элемент становится четвертым сыном. Но четырех сыновей ни у одного узла 2-3 дерева быть не может. Поэтому выполняется операция, называемая расщеплением. Узел node раcщепляется на два узла node и node'. Два наименьших элемента из четырёх становятся сыновьями узла node, два наибольших – сыновьями узла node'. Теперь нужно вставить узел node' среди сыновей узла p – родителя узла node. Здесь ситуация аналогичная. Если p имеет два сына, то node' становится третьим и помещается непосредственно справа от node. Если узел p уже имеет трёх сыновей, то он расщепляется на да узла p и p', узлу p приписываются два наименьших сына, узлу p' – оставшиеся. Затем вставляем узел p' среди сыновей родителя узла p и т.д.
На рис.5.16 показан первый этап вставки элемента 10 в 1-2 дерево из рис.5.15, на котором произошло расщепление узла с ключами 8 и 12 на два узла. В полученном дереве корень имеет четырех сыновей, следовательно, потребуется расщеплять корень.
Рис.5.16. Вставка элемента 10 в 2-3 дерево — первый этап
В этой ситуации создаётся новый корень, чьими сыновьями будут два узла, полученные в результате разбиения старого корня. При этом число уровней дерева увеличивается на 1. Обратим внимание, что это единственная ситуация, когда высота дерева увеличивается, но сбалансированность не нарушается— все пути от корня до любого листа по-прежнему имеют одинаковую длину (рис.5.17).
Рис.5.17. Вставка элемента 10 в 2-3 дерево — 2 этап (расщепление корня.)
Но в данной ситуации можно было бы обойтись без расщепления корня, если выполнить еще одну операцию над 2-3 деревьями, которая называется переливанием. Так, из рис.5.16 можно догадаться, что от одного из сыновей корня (это второй сын с ключом 8) можно избавиться, передав его первого сына левому брату, а второго сына— правому брату. Это возможно, поскольку у каждого брата только по два сына. Результат показан на рис.5.18.
Рис.5.18. Вставка элемента 10 в 2-3 дерево — 2 этап (переливание узлов)
Однако при попытке вставить любое новое значение в дерево на рис. 5.18 все равно придется выполнять расщепление корня, и высота дерева увеличится на 1.
Удаление элемента из 2-3-дерева.
Если у родителя три листа, то удаление проблем не представляет. Например, из дерева на рис.5.18 можно удалить любой лист без какого-либо дополнительного преобразования структуры.
Рассмотрим случай, когда у узла два листа. Если узел является корнем, то единственный сын становится новым корнем дерева. Пусть node не является корнем. p – его родитель.
Если p имеет другого сына, расположенного слева или справа от node и имеющего трёх сыновей, то один из них становится сыном узла node. Это уже известная операция переливания.
Если сын p имеет только двух сыновей, то единственный сын узла node присоединяется к этим сыновьям, а узел node удаляется. Такая операция является обратной расщеплению и называется склеиванием. Если после этого родитель p будет иметь только одного сына, то повторяется вышеописанный процесс с заменой node на p.
Например, удалим элемент 10 из рис.5.17.
Рис.5.19. Удаление элемента 10 из 2-3 дерева из рис.5.17
В данном случае была выполнена операция переливания.
Теперь удалим из полученного дерева узел 7. Выполнив операции переливания и склеивания, получим дерево, изображенное на рис.5.20.
Рис.5.20. Удаление элемента 7 из 2-3 дерева из рис.5.19
Реализация вышеописанных операций не сложна, но трудоемка, поэтому здесь не приводится.