- •Введение
- •Основные понятия и определения
- •Типы данных
- •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.4.3. Реализация бинарного дерева поиска
В подавляющем большинстве приложений используется ссылочная реализация дерева на основе указателей. Структура узла бинарного дерева в этом случае содержит, кроме ключа и связанных с ним данных, указатели на правого и левого сына. Поэтому введем новую структуру узла дерева node, которая содержит в качестве данных уже используемую ранее структуру item и два указателя.
Определим тип bst (binary seach tree — бинарное дерево поиска),
typedef node* bst;
который будет использоваться при определении первого параметра всех функций.
В листинге 5.3 содержится рекурсивный и нерекурсивный варианты реализации функций поиска и вставки и нерекурсивный вариант функции удаления (рекурсивный можно найти в [14]). Для полноты картины приведена функция ЛКП-обхода, которая выводит элементы дерева в порядке возрастания ключей, и функция, которая строит бинарное дерево поиска, заполненное случайными значениями, вызывая при этом функцию вставки.
Листинг 5.3. Реализация бинарного дерева поиска
#include <iostream.h>
#include <stdlib.h>
typedef int T_key; //тип ключа, может быть любым
typedef char T_data;//тип связанных данных, любой
struct item //структура элемента
{ T_key key; //ключ
T_data data; //связанные данные
};
const item nullitem={-1};//пустой элемент возвращается при промахе поиска
struct node // узел дерева
{item data; // данные
node *left, *right; // указатели на детей
node(item x) // конструктор для заполнения узла при создании
{data=x;left=right=NULL;}
};
typedef node* bst; //bst - binary seach tree
// ниже приводится реализация функций
// нерекурсивная функция поиска
item seach(bst root, T_key k)
{if (!root) return nullitem;
bst p=root;
while (p)
{ if (k==p->data.key) return p->data;
if (k<p->data.key) p=p->left;
else p=p->right;
}
return nullitem;
}
// рекурсивный вариант функции поиска
item seach_rec(bst root, T_key k)
{ if(!root) return nullitem; // дерево пусто - промах
if (k==root->data.key) return root->data; // поиск успешен
if (k<root->data.key) return seach_rec(root->left, k);
else return seach_rec(root->right, k);
}
// нерекурсивная функция вставки
bool insert(bst &root, item x)
{ if (!root) // дерево еще не заполнено
{ root=new node(x); if(root)return true; else return false;
}
bst p=root,parent; // parent родитель p
while (p) // находим место для вставки
{ parent=p;
if (x.key<p->data.key) p=p->left;
else p=p->right;
}
p= new node(x); // формируем новый элемент
//вставляем его как левого или правого сына
if (x.key<parent->data.key) parent->left=p;
else parent->right=p;
if (p) return true; else return false;
}
// рекурсивная функция вставки
bool insert_rec(bst &root, item x)
{ if (!root)// дерево пустое - терминальная ветвь
{ root=new node(x);if (root) return true; else return false;
}
if (x.key<root->data.key) return insert_rec(root->left,x);
else return insert_rec(root->right,x);
}
// функция удаления узла
bool remove(bst &root, T_key k)
{ if(!root) return false; // дерево пусто
bst p=root,parent=NULL;
// поиск удаляемого узла p и его родителя
while (p&&k!=p->data.key)
{ parent=p;
if (k<p->data.key) p=p->left;
else p=p->right;
}
if (!p) return false; // обработали промах
// удаляем лист
if (!p->left&&!p->right)
if(p==root) root=NULL; //может, в дереве всего один узел
else if (parent->left==p) parent->left=NULL;
else parent->right=NULL;
// удаляем узел, у которого только один сын
if (p->left&&!p->right||!p->left&&p->right)
{ bst q; // запомним указатель на сына
if (p->left) q=p->left; else q=p->right;
if(p==root) root=q; // у корня нет родителя
else // подсоединяем сына к дедушке, удаляя родителя
if (parent->left==p) parent->left=q;
else parent->right=q;
}
if (p->left&&p->right)// есть оба сына
{ //спускаемся в левое поддерево
bst t=p->left,parent=p; //parent-родитель t
while (t->right) {parent=t;t=t->right;}
//нашли крайнего правого сына t и его родителя parent
p->data=t->data; //заменили данные у удаляемого узла
// теперь удаляем крайнего правого сына t
if (!t->left) //он лист
parent->right=NULL;
else // у него есть левое поддерево
parent->right=t->left;
p=t; // теперь можно освобождать память для t
}
delete p; return true; //освободили память
}
// формирование бинарного дерева поиска из n случайных элементов
void randtree(bst &root, int n)
{ item x;
for (int i=0;i<n; i++)
{ x.key=rand()%1000; // случайные числа
x.data=rand()%26+65; // случайные заглавные латинские буквы
insert(root,x); //или insert_rec(root,x);
}
}
// вывод дерева в порядке возрастания ключей
void out(bst root)
{if (!root) return;
out(root->left);
cout<<root->data.key<<" "<<root->data.data<<"; ";
out(root->right);
}