
- •1.1. Элементы языка программирования
- •Основные правила записи программы:
- •1.2. Алфавит языка
- •1.3. Лексемы
- •1.4. Концепция данных
- •2.2. Операции
- •2.2.1. Арифметические операции
- •2.2.2. Операции присваивания
- •2.2.3. Операции отношения
- •2.2.4. Логические операции
- •2.2.5. Поразрядные операции
- •2.2.6. Вычисление выражений
- •3. Структурное программирование
- •3.1. Общая характеристика операторов
- •3.2. Оператор-выражение
- •3.3. Условный оператор
- •3.4. Составной оператор
- •3.5. Операторы для программирования циклов
- •3.5.1. Оператор цикла for
- •3.5.2. Оператор цикла while
- •3.5.3. Оператор цикла do while
- •3.5.4. Оператор break
- •3.5.5. Оператор continue
- •3.6. Оператор goto
- •3.7. Пустой оператор
- •3.8. Оператор switch
- •3.9. Оператор return
- •4. Массивы
- •4.1. Объявление массива
- •4.2. Обращение к элементам массива
- •4.3. Типовые алгоритмы работы с массивами
- •4.4. Многомерные массивы
- •5. Строки
- •5.1. Объявление строки
- •5.2. Посимвольная обработка строк
- •5.3. Ввод строк
- •5.4. Библиотечные функции для работы с текстом
- •6. Указатели
- •6.1. Объявление указателей
- •6.2. Операции над указателями
- •6.3. Связь между указателями и массивами
- •6.4. Функция strtok для выделения лексем из текста
- •6.5. Динамические массивы
- •7. Структуры и объединения
- •7.1. Объявление структуры
- •Компонент структуры может быть любого типа, кроме типа объявляемой структуры.
- •7.2. Операции над структурами
- •7.3. Объявление объединения
- •8. Модульное программирование
- •8.1. Нисходящее проектирование и программирование
- •8.2. Определение и вызов функции
- •8.3. Место определения функции в программе
- •8.4. Обмен данными между функциями
- •8.4.1. Использование глобальных переменных
- •8.4.2. Использование аппарата формальных и фактических параметров
- •8.4.3. Передача массивов в функцию
- •8.5. Перегрузка функции
- •8.6. Шаблон функции
- •8.7. Рекурсивные функции
- •8.8. Функции с параметрами по умолчанию
- •8.9. Передача в функцию другой функции
- •9. Работа с файлами
- •9.1. Текстовые и двоичные файлы
- •9.2. Объявление файловых переменных
- •9.3. Чтение текстового файла
- •9.4. Создание текстового файла
- •9.5. Изменение данных в текстовом файле
- •9.6. Вывод в двоичный файл
- •9.7. Чтение данных из двоичного файла
- •9.8. Изменение данных двоичного файла
- •9.9. Организация файла с произвольным доступом
- •10. Данные с динамической структурой
- •10.1. Линейный список
- •10.1.1. Специальные типы линейных списков
- •10.1.2. Реализация линейного списка с помощью массива
- •10.1.3. Реализация линейного списка с помощью связанного однонаправленного списка
- •10.1.4. Реализация линейного списка с помощью связанного двунаправленного списка
- •10.2. Деревья
- •10.2.1. Основная терминология
- •10.2.2. Реализация двоичных деревьев поиска Для реализации дерева поиска используются массивы и связанные указателями элементы [3, 4].
- •10.2.3. Сбалансированные деревья
- •Основные достоинства в-дерева:
- •10.3. Графы
- •10.3.1. Определения
- •10.3.2. Реализация графа с помощью списков смежности
- •10.3.3. Реализация графа с помощью матрицы смежности
- •10.3.4. Поиск кратчайших путей. Алгоритм Дейкстры
- •10.3.5. Матрица достижимости. Алгоритм Уоршалла
10.2.1. Основная терминология
Дерево – конечное множество элементов, образующих иерархическую структуру.
Элементы дерева называют вершинами (узлами). Отрезок, соединяющий вершины дерева, называется ветвью (дугой, ребром). Дерево состоит из вершин и ветвей. У дерева есть единственная вершина, называемая корнем его поддеревьев. В корень не входят ветви. Деревья изображаются корнем вверх. Лист – это вершина, из которой не выходят ветви. Уровень вершины – это путь от корня до вершины (количество ветвей). Глубина (высота) дерева – это максимальная длина пути от корня до листьев.
Вершина х называется родителем (отцом) для вершины у, если вершина у находится на один уровень ниже, чем вершина х, и связана с ней. Вершина у называется сыном вершины х. Предки вершины – это ее родитель и все родители родителей. Потомки вершины – это все ее сыновья все сыновья сыновей.
Такая терминология заимствована из генеалогических деревьев – родовой схемы. Родовая схема показывает потомков главы рода. Родовая схема – сильноветвящееся дерево. Сильноветвящееся дерево – это дерево, у которого из любой вершины могут выходить более двух ветвей.
Двоичное дерево – это дерево, из каждой вершины которого выходит не более двух ветвей. Примером двоичного дерева является генеалогическое дерево – родословная схема. Это дерево показывает всех предков данного лица.
Двоичное дерево поиска – это особым образом организованное двоичное дерево, в котором слева от каждой вершины находятся вершины с меньшим значением ключа данных, а справа – с большим и/ или равным. Предполагается, что данные, хранящиеся в вершинах, можно сравнивать. В дереве поиска могут храниться данные любого типа, допускающие сравнение. Пример дерева поиска приведен на рис. 1.
Рис. 1. Дерево поиска
Дерево поиска используется для быстрого поиска данных. Рассмотрим процесс поиска вершины со значением 4. Зная принцип построения дерева поиска, процесс поиска вершины в дереве становится целенаправленным: 8, 3, 6, 4. Для поиска любого данного в дереве поиска высотой h требуется не более h+1 сравнений. Для полного дерева из n вершин, у которого все вершины имеют двух сыновей, высота выражается формулой [3]:
h=log2n-1
При использовании дерева поиска из 1000000 элементов для поиска элемента потребуется порядка 20 сравнений.
10.2.2. Реализация двоичных деревьев поиска Для реализации дерева поиска используются массивы и связанные указателями элементы [3, 4].
Рассмотрим второй способ реализации дерева. В С++ вершину дерева можно представить как структуру, состоящую из информационного поля и двух указателей на левого и правого сыновей. Например, тип вершины двоичного дерева, загруженного целыми числами, можно объявить следующим образом:
struct element
{
int info; //информационное поле
rec *left; //адрес левого сына
element *right; //адрес правого сына
};
Основные операции над двоичным деревом поиска:
поиск вершины;
включение вершины;
удаление вершины;
обход дерева.
Пример программы, выполняющей операции над деревом поиска (добавление вершины, поиск вершины, обход дерева в прямом порядке):
#include <iostream.h>
#include <conio.h>
struct element //тип вершины дерева
{
int info;
element* left,*right;
};
//Поиск вершины по ключу key в дереве с корнем tree
element * seach(element * tree, int key); //возвращает адрес или 0
//Добавление вершины с ключом key в дереве с корнем tree
void insert(element * &tree, int key);
//Вывод дерева с корнем tree в прямом порядке
void view_cab(element * tree)
void main()
{
int n; //количество вершин в дереве
int x; //значение вершины
element * tree; //корень дерева
tree=0; //дерево пустое
cout<<"n "; cin >>n;
//Цикл построения дерева из n вершин
for(int i=1;i<=n; i++)
{
cout<<"x? ";
cin>>x;
insert(tree,x); //добавление вершины
}
view_cab(tree); //вывод дерева в прямом порядке
cout <<endl;
// Поиск вершины
cout<<"x? ";
cin>>x;
if (seach(tree,x) ==0)
cout<<"No"<<endl; //вершина не найдена
else
cout<<"Yes"<<endl;
getch();
}
element * seach(element * tree, int key)
{
element * t; //адрес текущей вершины
//Направленный обход узлов дерева, начиная с корня
t=tree;
while (t!=0 && t->info!=key) //выйти из цикла при совпадении //или после проверки листа
{
if (key<t->info) //поиск слева
t=t->left;
else //поиск справа
t=t->right;
}
return t; //при t=0 адрес не найден
}
void insert(element * &tree, int key)
{
element *p1,*p2; //адреса текущей и родительской вершин
//Создание вершины-листа
element * t= new element;
//Заполнение вершины
t->info=key;
t->left=0; //лист
t->right=0;
//Включение вершины в дерево
if (tree==0) //дерево пустое
tree= t; //сделать вершину корнем
else //дерево не пустое
{
p1=tree;
//Поиск места для вставки – адреса родительской вершины p2
while (p1!=0) //пока дерево (поддерево) не пустое
{
//Обновить указатель p2 перед изменением p1
p2=p1;
if (key<p1->info) //поиск слева
p1=p1->left;
else //поиск справа
p1=p1->right;
}
//Вставить элемент слева или справа от родителя с адресом р2
if (key<p2->info) //включить как левый лист родителя р2
p2->left=t;
else //включить как правый лист родителя р2
p2->right=t);
}
}
void view_cab(element * tree)
{
if( tree!=0) // если дерево не пустое
{
cout<<tree->info<<" "; //посетить корень
//Посетить левое дерево по тому же алгоритму
view_cab(tree->left); //рекурсивный вызов
//Посетить правое дерево по тому же алгоритму
view_cab(tree->right); ; //рекурсивный вызов
}
}
В программе используется способ обхода дерева в прямом порядке. Он реализован в виде рекурсивной функции. При обходе в прямом порядке вначале посещается корень, затем левое поддерево, а затем правое поддерево. Нерекурсивный алгоритм обхода дерева в прямом порядке приведен ниже. В нем используется стек для запоминания адресов правых поддеревьев при переходе к левому поддереву, чтобы к ним вернуться при завершении обхода левого дерева.
void viewcab1(element* tree)
{
int n; //текущее количество адресов в стеке
element* stack[20]; //стек для адресов необработанных правых //поддеревьев
element* p; //адрес текущей вершины дерева
n=0; // стек пуст
p=tree;
while (p!=0) //цикл обхода дерева
{
cout<<p->info<<' ';
if (p->right!=0) //есть правое дерево у текущей вершины
{stack[n]= p->right; n++;} //добавить адрес правого дерева в стек
if (p->left!=0) //есть левое дерево у текущей вершины
p=p->left; //идти к левому дереву
else //нет левого дерева – проверить стек
if (n!=0) // стек не пуст: не все обработано
{
//Взять адрес правого дерева из стека для обработки
p=stack[n-1]; n--;
}
else //стек пуст: все вершины обработаны
p=0;
}
cout<<endl;
}
Симметричный обход дерева поиска используется для вывода данных, загруженных в дерево поиска, в возрастающем порядке. При симметричном обходе вначале посещается левое поддерево, затем посещается корень, а затем правое поддерево.
Пример рекурсивной функции симметричного обхода дерева:
void view_acb(element* tree)
{
if( tree!=0)
{
view_acb(tree->left);
cout<<tree->info<<" ";
view_acb(tree->right);
}
}
Алгоритмы других способов обхода деревьев, а также алгоритм удаления вершины из дерева поиска рассмотрены в [1, 3, 4].