- •Содержание
- •1. Основные понятия и определения
- •1.1. Структура персональной ЭВМ
- •1.2. Размещение данных и программ в памяти ПЭВМ
- •1.3. Программные модули
- •1.4. Ошибки
- •1.5. Функциональная и модульная декомпозиции
- •1.6. Файловая система хранения информации
- •1.7. Операционная система
- •2. Понятие алгоритмов и способы их описания
- •2.1. Свойства алгоритмов
- •2.2. Способы описания алгоритмов
- •3. Базовые элементы языка Cи
- •3.1. Алфавит языка
- •3.2. Лексемы
- •3.3. Идентификаторы и ключевые слова
- •3.4. Знаки операций
- •3.5. Литералы (константы)
- •3.6. Комментарии
- •4. Базовые типы объектов
- •4.1. Простейшая программа
- •4.2. Основные типы данных
- •4.3. Декларация объектов
- •4.4. Данные целого типа (int)
- •4.5. Данные символьного типа (char)
- •4.6. Данные вещественного типа (float, double)
- •5. Константы в программах
- •5.1. Целочисленные константы
- •5.2. Константы вещественного типа
- •5.3. Символьные константы
- •5.4. Строковые константы
- •6. Обзор операций
- •6.1. Операции, выражения
- •6.2. Арифметические операции
- •6.3. Операции присваивания
- •6.4. Сокращенная запись операции присваивания
- •6.5. Преобразование типов операндов арифметических операций
- •6.6. Операция приведения типа
- •6.7. Операции сравнения
- •6.8. Логические операции
- •6.9. Побитовые логические операции, операции над битами
- •6.10. Операция «,» (запятая)
- •7. Обзор базовых инструкций языка Си
- •7.1. Стандартная библиотека языка Си
- •7.2. Стандартные математические функции
- •7.3. Функции вывода данных
- •7.4. Функции ввода информации
- •7.5. Ввод - вывод потоками
- •8. Синтаксис операторов языка Cи
- •8.1. Условные операторы
- •8.2. Условная операция «? :»
- •8.3. Оператор выбора альтернатив (переключатель)
- •9. Составление циклических алгоритмов
- •9.1. Понятие цикла
- •9.2. Оператор с предусловием while
- •9.3. Оператор цикла с постусловием do – while
- •9.4. Оператор цикла с предусловием и коррекцией for
- •10. Операторы передачи управления
- •10.1. Оператор безусловного перехода goto
- •10.2. Оператор continue
- •10.3. Оператор break
- •10.4. Оператор return
- •11.1. Операции над указателями (косвенная адресация)
- •11.2. Ссылка
- •12. Массивы
- •12.1. Одномерные массивы
- •12.2. Многомерные массивы
- •12.3. Операция sizeof
- •12.4. Применение указателей
- •12.5. Указатели на указатели
- •13. Работа с динамической памятью
- •13.1. Пример создания одномерного динамического массива
- •13.2. Пример создания двухмерного динамического массива
- •14. Строки в языке Си
- •14.1. Русификация под Visual
- •15. Функции пользователя
- •15.1. Декларация функции
- •15.2. Вызов функции
- •15.3. Операция typedef
- •15.4. Указатели на функции
- •16. Классы памяти и области действия объектов
- •16.1. Автоматические переменные
- •16.2. Внешние переменные
- •16.3. Область действия переменных
- •17. Структуры, объединения, перечисления
- •17.1. Структуры
- •17.2. Декларация структурного типа данных
- •17.3. Создание структурных переменных
- •17.4. Вложенные структуры
- •17.5. Массивы структур
- •17.6. Размещение структурных переменных в памяти
- •17.7. Объединения
- •17.8. Перечисления
- •18. Файлы в языке Си
- •18.1. Открытие файла
- •18.2. Закрытие файла
- •18.3. Запись – чтение информации
- •18.4. Текстовые файлы
- •18.5. Бинарные файлы
- •Литература
- •Приложение 1. Таблицы символов ASCII
- •Приложение 2. Операции языка Си
- •Приложение 3. Возможности препроцессора
- •1. Вид рабочего стола консольного приложения Visual C++
- •2. Создание нового проекта
- •3. Добавление к проекту файлов с исходным кодом
- •4. Компиляция, компоновка и выполнение проекта
- •5. Конфигурация проекта
- •6. Открытие существующего проекта
- •Приложение 5. Некоторые возможности отладчика Visual C++
- •1. Установка точки прерывания
- •2. Пошаговое выполнение программы
- •3. Проверка значений переменных во время выполнения программы
- •4. Окна Auto и Watch 1
- •5. Программные средства отладки
- •ЧАСТЬ 2
- •1. Системы счисления
- •1.1. Общие определения
- •1.2. Алгоритмы перевода из одной системы счисления в другую
- •2. Организация памяти и структуры данных
- •3. Линейные списки и рекурсия
- •4. Динамическая структура данных – СТЕК
- •5. Динамическая структура данных – ОЧЕРЕДЬ
- •6. Двунаправленный линейный список
- •7. Построение обратной польской записи
- •8. Нелинейные структуры данных
- •Пояснение к программе
- •9. Понятие хеширования
- •9.1. Хеш-функция и хеш-таблица
- •9.2. Схемы хеширования
- •10. Элементы теории погрешностей
- •10.1. Методы реализации математических моделей
- •10.2. Источники погрешностей
- •10.3. Приближенные числа и оценка их погрешностей
- •10.4. Прямая задача теории погрешностей
- •10.5. Обратная задача теории погрешностей
- •10.6. Понятия устойчивости, корректности и сходимости
- •11. Вычисление интегралов
- •11.1. Формулы численного интегрирования
- •11.2. Формула средних
- •11.3. Формула трапеций
- •11.4. Формула Симпсона
- •11.5. Схема с автоматическим выбором шага по заданной точности
- •12. Методы решения нелинейных уравнений
- •12.1. Итерационные методы уточнения корней
- •12.2. Метод Ньютона
- •12.3. Метод секущих
- •12.4. Метод Вегстейна
- •12.5. Метод парабол
- •12.6. Метод деления отрезка пополам
- •13. Решение систем линейных алгебраических уравнений
- •13.1. Прямые методы решения СЛАУ
- •13.2. Метод Гаусса
- •13.3. Метод прогонки
- •13.4. Метод квадратного корня
- •13.5. Итерационные методы решения СЛАУ
- •13.6. Метод простой итерации
- •14. Поиск и сортировка
- •14.1. Последовательный поиск в массиве
- •14.2. Барьерный последовательный поиск
- •14.3. Сортировка вставками (включениями)
- •14.4. Двоичный поиск
- •14.5. Сортировка выбором
- •14.6. Алгоритмы сортировки выбором
- •14.7. Сортировка обменом
- •14.8. Быстрая сортировка
- •15. Обработка статистических данных
8. Нелинейные структуры данных
Мы рассмотрели связные структуры данных или списки – это линейные структуры динамических списковых данных.
Введение в динамическую переменную двух и более «полей-указателей» позволяет получить нелинейные структуры данных. Наиболее распространенные:
а) «текст» – переменная с тремя полями, «первое поле» – информация (i), «второе» – либо указывает на первый элемент следующей строки, либо имеет значение NULL (N), «третье» либо на следующий элемент текущей строки, либо «обнулено»:
i |
|
|
|
|
|
|
|
|
i |
|
N |
|
|
|
|
|
i |
|
N |
N |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
i |
N |
N |
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i |
N |
|
|
|
|
|
|
|
i |
N |
N |
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
б) довольно часто при работе с данными бывает удобно использовать структуры с иерархическим представлением, изображенные следующим образом:
Корень дерева
Узлы
x
Y
Листья
Такая конструкция данных получила название «дерево».
Дерево состоит из элементов, называемых узлами (вершинами). Узлы соединены между собой направленными дугами. В случае X→Y вершина X называется предком (родителем), а Y – потомком (сыном).
Дерево имеет единственный узел, у которого нет предков. Такой узел называют корнем. Любой другой узел имеет ровно одного предка. Узел, не имеющий потомков, называется листом.
Внутренний узел – это узел, не являющийся ни листом ни корнем. Порядок узла – количество его узлов-потомков. Степень дерева – максимальный порядок его узлов. Глубина узла равна числу его предков плюс один. Глубина дерева – это наибольшая глубина его узлов.
Бинарные деревья
Бинарное дерево – это динамическая структура данных, состоящая из узлов, каждый из которых содержит, кроме данных, не более двух ссылок на бинарные
143
поддеревья, т.е. у каждого узла (предка) может быть не более двух потомков – левый и правый. На каждый узел бинарного дерева имеется ровно одна ссылка.
Начальный же узел, как уже известно, называется корнем дерева.
Корень дерева
Root
Левое поддерево |
Правое поддерево |
Left |
Right |
На рисунке приведен пример бинарного дерева (корень обычно изображается сверху, хотя изображение можно и перевернуть).
Такая структура данных организуется следующим образом:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
i |
N |
N |
|
|
|
|
i |
N |
N |
|
|
|
|
i |
N |
N |
|
|
|
i |
N |
N |
|||||||||||||
|
|
|
|
|
|
|
|||||||||||||||||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
или, соблюдая более привычную форму:
|
|
|
info |
|
|
|
|
Left |
|
|
|
Right |
|||
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
info |
|
|
|
|
info |
|
|
|
|
|
|
|
|
|
info |
|
info |
|
info |
|
info |
|||
|
|
|
|
|
|
|
|
|
|
N |
N |
|
N |
N |
|
N N |
|
N |
N |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Высота дерева, как и раньше, определяется количеством уровней, на которых располагаются его узлы.
Если дерево организовано так, что для каждого узла все ключи его левого поддерева меньше ключа этого узла, а все ключи его правого поддерева – больше, оно называется деревом поиска. Одинаковые ключи здесь не допускаются.
Представление динамических данных в виде древовидных структур оказывается довольно удобным и эффективным для решения задач быстрого поиска информации.
144
В дереве поиска можно найти элемент по ключу, двигаясь от корня, и, переходя на левое или правое поддерево, в зависимости от значения ключа в каждом узле. Такой поиск гораздо эффективнее поиска по списку, поскольку время поиска определяется высотой дерева, а она пропорциональна логарифму количества узлов – log2N. Время поиска по сравнению с линейной структурой сокращается с
N до log2N.
Для так называемого сбалансированного дерева, в котором количество узлов справа и слева отличается не более чем на единицу, высота дерева как раз и равна двоичному логарифму количества узлов. Линейный список можно представить как вырожденное бинарное дерево, в котором каждый узел имеет не более одной ссылки. Для списка среднее время поиска равно ровно половине длины этого списка.
Дерево по своей организации является рекурсивной структурой данных, поскольку каждое его поддерево также является деревом. Действия с такими структурами изящнее всего описываются с помощью рекурсивных алгоритмов. Например, функцию обхода всех узлов дерева можно в общем виде описать так:
type way_around (дерево) {
way_around (левое поддерево); посещение корня;
way_around (правое поддерево);
}
Можно обходить дерево и в другом порядке, например, сначала корень, потом поддеревья, но приведенная функция позволяет получить на выходе отсортированную последовательность ключей, поскольку сначала посещаются вершины с меньшими ключами, расположенные в левом поддереве, а потом вершины правого поддерева с большими ключами. На приведенном ниже рисунке это Путь «1».
10 |
Информационная |
|
info |
часть |
|
Ключевое |
||
|
||
|
поле |
|
6 |
25 |
|
info |
info |
|
1 |
8 |
20 |
30 |
|
Путь «2» |
info |
info |
info |
info |
|
0 0 |
0 0 |
0 |
0 0 |
Путь «1» |
|
|
|
|
|
|
|
|
|
|
21 |
|
|
|
|
|
info |
|
|
|
|
|
0 |
0 |
|
145
Результат обхода дерева, изображенного на рисунке:
Путь «1»: 1→ 6→ 8→ 10→ 20→ 21→ 25→ 30
Если в функции обхода первое обращение идет к правому поддереву, результат обхода будет таким:
Путь «2»: 30→ 25→ 21→ 20→ 10→ 8→ 6→ 1
Таким образом, деревья поиска можно применять для сортировки значений (при обходе дерева узлы не удаляются).
Рассмотрим основные алгоритмы работы с бинарным деревом
Необходимо уметь:
–построить (создать) дерево;
–вставить новый элемент;
–обойти все элементы дерева, например, для просмотра или чтобы произвести некоторую операцию;
–выполнить поиск элемента с указанным значением в узле;
–удалить заданный элемент.
Обычно бинарное дерево строится сразу упорядоченным, т.е. узел левого сына имеет значение меньшее, чем значение родителя, а узел правого сына – большее. Например, если приходят числа 17, 18, 6, 5, 9, 23, 12, 7, 8, то построенное по ним дерево будет выглядеть следующим образом (для упрощения приводим только ключи):
|
|
|
|
|
|
|
|
17 |
|
|
1: 18>17 |
|
|
|||||
|
|
2: 6<17 |
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
18 |
|
|
|
|
|
|
3: 5<17, |
5<6 |
|
|
|
4: 9<17, |
9>6 |
|
5: 23>17, |
23>18 |
|||||||||
|
|
5 |
|
|
|
9 |
|
|
|
|
|
23 |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
7: 7<17, 7>6, 7<9 |
|
6: |
12<17, |
12>6, 12>9 |
|
|
7 12
8: 8<17, 8>6, 8<9, 8>7
8
Для того чтобы вставить новый элемент в дерево, необходимо найти для него место. Для этого, начиная с корня, сравниваем значения узлов (Y) со значением нового элемента (New). Если New<Y, то идем по левой ветви, в противном случае
– по правой ветви. Когда дойдем до узла, из которого не выходит нужная ветвь для дальнейшего поиска, это означает, что место под новый элемент найдено.
Путь поиска места в построенном дереве для числа 11:
146
11<17 |
|
17 |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
11>6 |
|
|
|
18 |
|
|
|||
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
9 |
|
|
|
|
|
23 |
|
|
|
|
11>9 |
|
|
|
|
|||||
|
|
|
|
|
|
|
|
11< 12 и из узла (12) |
|||
|
7 |
|
|
|
12 |
|
|
||||
|
|
|
|
|
|
|
|
|
|
нет ветви |
|
|
|
|
|
|
|
|
|
|
|
8 11
При удалении узла из дерева возможны три ситуации:
1. Удаляемый узел является листом – просто удаляем ссылку на него; Удаление листа с ключом key:
|
5 |
|
5 |
|
|
Получим: |
|
key |
8 |
NULL |
8 |
2. Из удаляемого узла выходит только одна ветвь; Удаление узла имеющего одного потомка:
5 |
5 |
|
Получим: |
key |
7 |
7 NULL
3.Удаление узла, имеющего двух потомков, значительно сложнее. Если key
–исключаемый узел, то его следует заменить узлом w, который содержит либо наибольший ключ в левом поддереве, либо наименьший ключ в правом поддереве. Такой узел w является либо листом, либо самым правым узлом поддерева key, у которого имеется только левый потомок:
147
|
|
key |
Удалить узел (7) |
|
|
|
|
|
|
7 |
|
|
6 |
|
|
|
|
Получим |
|
|
|
||
|
|
|
|
|
|
|
|
4 |
|
|
9 |
|
4 |
|
9 |
|
|
w |
|
|
|
|
|
2 |
6 |
8 |
10 |
2 |
5 |
8 |
10 |
5 |
|
NULL |
|
|
|
NULL |
|
Таким образом, если из удаляемого узла выходит две ветви (в данном случае на место удаляемого узла надо поставить либо самый правый узел левой ветви, либо самый левый узел правой ветви для сохранения упорядоченности дерева).
Например, построенное дерево после удаления узла 6 может стать таким:
17
|
|
|
|
|
|
|
|
|
|
|
Правое поддерево |
|
|
7 |
|
|
|
|
|
|
18 |
||
|
|
|
|
|
|
|
|
|
|
|
Right |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
9 |
|
|
|
|
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
|
12 |
|
|
|
|||
Левое поддерево |
|
|
|
|
|
|
|
|
|
|
|
Left |
|
|
|
11 |
|
|
|
|
|
Рассмотрим задачу обхода дерева. Существуют три алгоритма обхода деревьев, которые естественно следуют из самой структуры дерева.
1)Обход слева направо: Left-Root-Right (сначала посещаем левое поддерево, затем – корень и, наконец, правое поддерево).
2)Обход сверху вниз: Root-Left-Right (посещаем корень до поддеревьев).
3)Обход снизу вверх: Left-Right-Root (посещаем корень после поддеревьев). Интересно проследить результаты этих трех обходов на примере записи
формулы в виде дерева, так как они и позволяют получить различные формы записи арифметических выражений.
Рассмотрим на примере формулы: ((a+b/c)*(d–e*f )). Дерево формируется по принципу:
–в корне размещаем операцию, которая выполнится последней;
–далее узлы – операции, операнды – листья дерева.
148
*
|
|
+ |
|
|
|
|
|
|
|
- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a |
|
|
/ |
|
|
|
|
d |
|
|
* |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b |
|
|
c |
|
|
|
|
e |
|
f |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Обход 1 (Left-Root-Right) дает обычную инфиксную запись выражения (без скобок):
a + b / c * d – e * f .
*
|
|
+ |
|
|
|
|
|
|
|
- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a |
|
|
/ |
|
|
|
|
d |
|
|
* |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b |
|
|
c |
|
|
|
|
e |
|
f |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Обход 2 (Root-Left-Right) – префиксную запись выражения (без скобок):
|
|
|
|
|
|
|
* + a / b c – d * e f |
. |
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ |
|
|
|
|
|
- |
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a |
|
/ |
|
|
|
|
|
d |
|
|
|
* |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b |
|
c |
|
|
|
|
e |
|
f |
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Обход 3 (Left-Right-Root) – постфиксную (ПОЛИЗ – польская инверсная запись):
a b c / + d e f * – * .
Пример. Сформировать дерево из целых чисел, выполнить просмотр, добавление, удаление по ключу и освобождение памяти при выходе из программы.
Для каждого рекурсивного алгоритма можно создать его не рекурсивный эквивалент, и в приведенной ниже программе реализован не рекурсивный алгоритм поиска по дереву с включением и рекурсивная функция обхода дерева. При-
149
мер построения бинарного дерева, осуществляя поиск элемента с заданным ключом и, если элемент не найден – включает его в соответствующее место дерева. Для включения элемента необходимо помнить пройденный по дереву путь на один шаг назад и знать, выполняется ли включение нового элемента в левое или правое поддерево его предка.
Рекурсии удалось избежать, сохранив всего одну переменную (Prev), и, повторив, при включении операторы, определяющие, к какому поддереву присоединяется новый узел.
В функции обхода дерева вторым параметром передается переменная, определяющая, на каком уровне (level) находится узел. Корень находится на уровне «0». Значения узлов выводятся по горизонтали так, что корень находится слева. Перед значением узла для имитации структуры дерева выводится количество пробелов, пропорциональное уровню узла. Если закомментировать цикл печати пробелов, вводимые значения ключей будут выведены в столбик.
Текст программы: |
|
#include <stdio.h> |
|
#include <conio.h> |
|
struct Tree { |
|
int info; |
|
Tree *Left, *Right; |
|
} *Root; |
// Root – указатель на корень |
void Make(int);
void Print (Tree*, int); void Del(int);
void Del_All(Tree*); Tree* List(int);
void main() {
int b, found, key;
// b – для ввода ключей, found – код поиска, key – удаляемый ключ while(1) {
puts(" Creat - 1\n View - 2\n Add - 3 \nDel Key - 4\n EXIT - 0"); switch (getch()) {
case '1': Make(0); break;
case '2': if( Root == NULL ) puts ("\t END TREE !"); else Print(Root, 0);
break;
case '3': if(Root==NULL) Make(0); else Make(1);
break;
case '4': puts("\n Input Del Info "); scanf("%d", &key); Del(key);
break;
case '0': Del_All(Root); puts("\n Tree Delete!");
150
return;
}// End switch
}// End while(1)
}
//============ Создание дерева ==========================
void Make(int kod) |
{ |
|
|
Tree *Prev, *t, *t1; |
|
|
|
int b,found; |
|
|
|
if ( kod == 0 ) |
{ |
|
// Формирование первого элемента |
puts( "\n Input Root :"); |
|
||
scanf("%d", &b); |
|
|
|
Root = List(b); |
// Установили указатель корня |
||
} |
|
|
|
//============= Вставка остальных элементов =============== |
|||
while(1) { |
|
|
|
puts( "\n Input Info :"); |
scanf("%d", &b); |
||
if (b<0) break; |
|
|
|
t = Root; |
|
|
|
found = 0; |
|
|
|
while ( t && !found ) |
{ |
||
Prev = t; |
|
|
|
if( b == t->info) found = 1; |
else
if ( b < t -> info ) t = t -> Left;
else |
t = t -> Right; |
|
} |
|
|
if (!found) { |
|
|
t1 = List(b); |
|
// Создаем новый узел |
if ( b < Prev -> info ) |
Prev -> Left = t1; |
|
else |
Prev -> Right = t1; |
|
} |
|
|
} // Конец цикла
}
//============= Удаление элемента по ключу (не корень) ===============
void Del(int key) {
Tree *Del,*Prev_Del,*R,*Prev_R;
//Del, Prev_Del - удаляемый элемент и его предок;
//R, Prev_R - элемент, на который заменяется удаленный и его предок;
|
Del = Root; Prev_Del = NULL; |
//-------- |
Поиск удаляемого элемента и его предка по ключу key--------------------- |
|
while (Del != NULL && Del -> info != key) { |
|
Prev_Del = Del; |
|
if (Del->info >key) Del=Del->Left; |
|
else Del=Del->Right; |
151
|
} |
|
|
if (Del==NULL){ |
// В дереве такого ключа нет |
|
puts ( "\n NO Key!"); |
|
|
return; |
|
|
} |
|
// |
--------------------- Поиск элемента для замены R ---------------------------------------- |
|
//---------- |
1. Если удаляемый элемент имеет одного потомка, или ЛИСТ ----------- |
|
|
if (Del -> Right == NULL) R = Del->Left; |
|
|
else |
|
|
if (Del -> Left == NULL) R = Del->Right; |
|
|
else { |
|
//---------------- |
Иначе, ищем самый правый узел в левом поддереве------------------ |
|
|
Prev_R = Del; |
|
|
R = Del - >Left; |
|
|
while (R->Right != NULL) { |
|
|
Prev _ R=R; |
|
|
R=R - >Right; |
|
|
} |
|
//------------------ |
2. Если удаляемый элемент имеет одного потомка------------------ |
|
|
if( Prev_R == Del) R->Right=Del->Right; |
|
|
else { |
|
//------------------ |
3. Если удаляемый элемент имеет двух потомков ------------------ |
|
|
R->Right=Del->Right; |
Prev_R->Right=R->Left; R->Left=Prev_R;
}
}
// Устанавливаем связь с предком удаляемого (Prev_Del) и заменой (R): if (Prev_Del==NULL) { Root = Prev_Del = R; }
else
if (Del->info < Prev_Del->info) Prev_Del->Left=R; else Prev_Del->Right=R;
printf("\n Delete %d element ",Del->info); delete Del;
}
//============= Формирование (создание) элемента - листа =============
Tree* List(int i) {
Tree *t = new Tree; // Захват памяти t -> info = i;
t -> Left = t -> Right = NULL; return t;
}
//=========== Функция вывода на экран ========================
void Print ( Tree *p, int level ) { if ( p ) {
152