Сортировка с помощью бинарного дерева.
Сортировка с помощью бинарного дерева. Рассмотрим применение функций работы с динамической памятью и динамическими информационными структурами на примере сортировки с помощью бинарного дерева. Выбор этого способа сортировки определен следующими соображениями.
1. Структуры данных типа "дерево" являются динамическими и важны при построении информационных и телекоммуникационных систем.
2. При сортировке произвольного набора данных заранее не известен его объем, и применение статической памяти, которая выделяется на этапе трансляции, может оказаться неоправданным в силу возникающих несоответствий между количеством
данных, подлежащих сортировке, и объемом статически выделенной памяти.
3. Выбранный способ сортировки позволяет освоить столь важные при работе с деревьями операции, как построение дерева и его обход, например, для печати содержимого его вершин.
Описание методов сортировки и работы с динамическими структурами данных можно найти в приведенной выше работе Н. Вирта. В качестве метода для программной реализации определим бинарную сортировку включением. Этот метод сортировки предполагает, что вводимые данные запоминаются в виде бинарного дерева (рис. 8.12). Дерево начинается с корня (root), который указывает на первую вершину. Из каждой вершины дерева выходят две ветви, связывающие ее с вершинами нижнего уровня.
Рис. 8.12. Пример заполненного бинарного дерева
При построении дерева на рис. 8.12 был использован произвольный набор английских слов, записанный в файл. Слова располагаются в файле в следующем порядке:
Выбранный метод сортировки определяет следующую процедуру формирования дерева. На первом от корня месте располагается первое слово, введенное из исходного файла. От него проводятся две ветви (левая и правая), ведущие к двум вершинам нижнего уровня. Если следующее слово в алфавитном порядке выше первого слова, то оно располагается в вершине нижнего уровня, к которой ведет левая ветвь (рис. 8.13).
Рис. 8.13. Размещение в дереве нового слова
Следующий этап построения дерева вновь начинаем с корневой вершины. Сравниваем слова "array" и "line". Строка "array" выше в алфавитном порядке строки "line", поэтому переходим к вершине нижнего уровня по левой ветви. Сравниваем строки "array" и "comma". Строка "array" также выше в алфавитном порядке строки "comma", поэтому от вершины, содержащей строку "comma", вновь спускаемся к вершине нижнего уровня по левой ветви. Итог третьего этапа показан на рис. 8.14.
Рис. 8.14. Дерево после трех включений
Результат ввода следующей строки "name" приведен на рис. 8.15.
Рис. 8.15. Дерево после четырех включений
Первое же сравнение строк "line" и "name" показывает, что "name" располагается в алфавитном порядке ниже "line", поэтому из вершины "line" необходимо выйти по правой ветви. Эта ветвь не ведет ни к какой вершине, поэтому к ней и присоединяется вершина нижнего уровня со строкой "name" (см. рис. 8.15).
Итогом описанного процесса будет бинарное дерево, показанное на рис. 8.12.
После того как мы построили бинарное дерево на бумаге, перейдем к его описанию на языке Си. Каждая вершина должна содержать символьный массив для представления слова в виде строки и указатели на две вершины нижнего уровня - для левой и правой ветвей. Определение структурного типа для структур, представляющих вершины дерева, может быть, например, таким:
С учетом приведенного определения структурного типа вершину дерева можно изобразить графически (рис. 8.16).
Рис. 8.16. Структурное представление вершины бинарного дерева
Как правило, заранее не известны длина каждого конкретного слова - массива sir и количество слов в исходном файле. В силу этого целесообразно динамически выделять память как для каждой вершины дерева, так и для собственно массива, хранящего слово в виде строки.
Модифицируем описание структурного типа, соответствующего вершине дерева, следующим образом:
Теперь в структуре типа struct node вместо массива символов фиксированной длины для слова из сортируемого файла используется указатель на строку. Память для каждой строки (массива типа char [ ]) необходимо запрашивать у операционной системы в соответствии с длиной конкретного слова.
Приведенному описанию соответствует графическое представление, изображенное на рис. 8.17.
Рис. 8.17. Модифицированное структурное представление вершины бинарного дерева
С учетом приведенного определения структурного типа для вершин бинарного дерева нарисуем еще раз (рис. 8.18) дерево, приведенное выше на рис. 8.12.
На рис. 8.18 указателям, не содержащим адреса какой-либо вершины, присвоено значение NULL; указатель root типа struct node * содержит адрес начальной вершины дерева.
Рис. 8.18. Структурное представление бинарного дерева, полученное в результате сортировки строк
Уточним алгоритм ввода очередного слова и построения соответствующей вершины.
Введя слово, необходимо, двигаясь от начальной вершины:
1) сравнить введенное слово со словом, хранящимся в данной вершине дерева (на него указывает str);
2) если введенная строка (слово) в алфавитном отношении расположена выше, то следует двигаться по левой ветви к вершине нижнего уровня, иначе - по правой ветви, тоже вниз;
3) если достигли конца пути, т.е. указатель, на вершину нижнего уровня равен NULL, то надо выполнить операции по созданию и подключению к дереву новой вершины, а именно:
• с помощью функции malloc( ) запросить память для вершины в виде структуры типа struct node;
• с помощью функции malloc( ) запросить память для представления очередного слова в виде строки;
• записать в полученный участок памяти введенную строку (слово);
• установить значения указателей новой вершины (в структуре типа struct node):
- указатель str - на участок памяти, выделенный для очередной строки со словом;
- указателям left и right присвоить значение NULL, так как ни один из них пока не адресует вершину нижнего уровня;
• записать в указатель left или right предыдущей вершины (на пройденном от начальной вершины пути) адрес участка памяти, выделенного под новую вершину дерева.
Объединим действия, описанные выше в пункте 3, в функцию new_node( ) - "Создать новую вершину". Алгоритм в целом, т.е. действия пунктов 1 3, оформим в виде функции add_node( ), которая будет вызывать функцию new_node( ).
Главная функция, в которой будет вызываться функция add_node( ), должна выполнить следующие действия:
• получить имя файла с данными для сортировки;
• открыть файл для чтения;
• инициализировать нулевым значением (NULL) указатель root на начальную вершину дерева;
• в цикле (до исчерпания данных из файла): читать очередную строку (слово) из файла и вызывать для каждой строки функцию add_node( ).
Теперь можно приступить к написанию текстов всех указанных функций. Начнем с функции main( ):
Текст функции main( ) не сложен и не требует пояснений. Функция print( ), вызываемая перед оператором return, предназначена для печати результатов сортировки; она рассматривается ниже.
Функции add_node( ), которая вызывается в теле цикла while, необходимо передать два параметра: прочитанную из файла строку (слово) line[ ] и указатель root на корневую вершину в дереве, так как при включении в дерево новой вершины просмотр дерева каждый раз начинается с корневой вершины.
Ниже приводится текст функции add_node( ).
Если указатель root на корневую вершину дерева имеет значение NULL (дерево пустое), то вызывается функция new_node( ), которая создает первую (корневую) вершину дерева и записывает в указатель root адрес этой вершины.
Если же указатель root имеет значение, отличное от NULL (в дереве существует хотя бы одна вершина), то выполняется цикл спуска по ветвям дерева до вершины, содержащей нулевой адрес в соответствующей ветви, и вызывается функция new_node( ) для создания новой вершины. Рассмотрим подробнее этот процесс для случая, когда в дереве присутствуют две вершины (рис. 8.19) и "подключается" слово "array".
Рис. 8.19. Подключение к дереву новой вершины
Цифрами 1 и 2 показаны те вершины и указатели ветвей, адреса которых содержат указатели ptr и prior, соответственно, после первого (1) и второго (2) выполнение цикла while. Указатель ptr содержит адрес текущей вершины.
Конструкция **prior определяет переменную prior как указатель на указатель. Перед чтением третьего слова из файла указатель prior будет содержать адрес элемента ptr->left, в который вместо NULL необходимо записать адрес новой вершины дерева, так как введенное слово (array) находится выше в алфавитном порядке, чем слово из второй вершины дерева (comma). Указателю ptr присваивается после второго выполнения цикла значение NULL, так как ниже второй вершины других вершин нет.
Напомним, что функция new_node( ) должна выполнить следующие действия:
• запросить динамическую память для новой вершины дерева (для структуры struct node);
• проинициализировать указатели left и right созданной структуры значениями NULL;
• запросить память в виде массива типа char[ ] для сохранения введенной из файла строки (слова);
• вернуть в функцию add_node( ) адрес участка памяти, где размещена новая вершина.
В функцию new_node( ) необходимо передать один аргумент - вновь введенную строку из файла. Текст функции new_node( ) приводится ниже.
Операция sizeof, используемая при вызове функции malloc( ), вычисляет в байтах длину структуры, описывающей вершину дерева. Выражение (strlen(line)+l) в следующей строке в качестве параметра функции malloc( ) задает размер необходимой для хранения введенной строки, добавляя один байт для записи в конце строки '\0'.
- Предисловие
- Раздел 1. Полный курс программирования на стандартном языке Си Глава 1. Базовые понятия языка
- 1.1. Алфавит, идентификаторы, служебные слова Алфавит
- Идентификатор
- Служебные (ключевые) слова
- 1.2. Константы и строки
- Символы, или символьные константы.
- Целые константы.
- Вещественные константы.
- Предельные значения и типы арифметических констант.
- Целые константы и выбираемые для них типы
- Данные вещественных типов
- Нулевой указатель.
- Строки, или строковые константы.
- 1.3. Переменные и именованные константы Переменная как объект.
- Определение переменных.
- Предельные значения переменных.
- Основные типы данных
- Инициализация переменных.
- Именованные константы.
- 1.4. Операции
- Знаки операций.
- Приоритеты (ранги) операций
- Унарные (одноместные) операции.
- 1.5. Разделители
- Квадратные скобки.
- Круглые скобки.
- Запятая.
- Точка с запятой.
- Двоеточие.
- Многоточие.
- Звездочка.
- Обозначение присваивания.
- Признак препроцессорных средств.
- 1.6. Выражения и приведение арифметических типов
- Отношения и логические выражения.
- Присваивание (выражение и оператор).
- Приведение типов.
- Правила преобразования типов
- Правила стандартных арифметических преобразований
- Выражения с поразрядными операциями.
- Условное выражение.
- Глава 2. Введение в программирование на языке си
- 2.1. Структура и компоненты простой программы
- Текст программы и препроцессор.
- Структура программы.
- Функция форматированного вывода.
- Программы печати предельных констант.
- Применимость вещественных данных.
- Выделение лексем из текста программы.
- 2.2. Элементарные средства программирования Деление операторов языка Си на группы.
- Программа оценки машинного нуля.
- Трассировочная таблица
- Ввод данных.
- Вычисление объема цилиндра.
- Сумма членов ряда Фибоначчи.
- 2.3. Операторы цикла Три формы операторов цикла.
- Приближенное значение экспоненты.
- Оператор break.
- Сумма отрезка степенного ряда.
- Оператор continue.
- Суммирование положительных чисел.
- 2.4. Массивы и вложение операторов цикла Массивы и переменные с индексами.
- Вычисление среднего и дисперсии.
- Упорядочение в одномерных массивах.
- Инициализация массивов.
- 2.5. Функции Определение функций.
- Функция для вычисления объема цилиндра.
- Функция для вычисления скалярного произведения векторов.
- Обращение к функции и ее прототип.
- Вычисление биномиального коэффициента.
- Вычисление объема цилиндра
- Вычисление площади треугольника.
- Скалярное произведение векторов.
- 2.6. Переключатели
- Глава 3. Препроцессорные средства
- 3.1. Стадии и команды препроцессорной обработки
- Стадии препроцессорной обработки.
- Директивы препроцессора.
- 3.2. Замены в тексте Директива #define.
- Цепочка подстановок.
- 3.3. Включение текстов из файлов
- 3.4. Условная компиляция Директивы ветвлений.
- Операция defined.
- 3.5. Макроподстановки средствами препроцессора
- Моделирование многомерных массивов.
- Отличия макросов от функций.
- Препроцессорные операции в строке замещения.
- 3.6. Вспомогательные директивы
- Препроцессорные обозначения строк.
- Реакция на ошибки.
- Пустая директива.
- Прагмы.
- 3.7. Встроенные (заранее определенные) макроимена
- Глава 4. Указатели, массивы, строки
- 4.1. Указатели на объекты Адреса и указатели.
- Операции над указателями.
- Арифметические операции и указатели.
- Указатели и отношения.
- 4.2. Указатели и массивы Указатели и доступ к элементам массивов.
- Массивы динамической памяти.
- Функции для выделения и освобождения памяти
- Массивы указателей и моделирование многомерных массивов.
- "Матрица" со строками разной длины.
- 4.3. Символьная информация и строки
- Ввод-вывод символьных данных.
- Внутренние коды и упорядоченность символов.
- Строки, или строковые константы.
- Строки и указатели.
- Глава 5. Функции
- 5.1. Общие сведения о функциях Определение функции.
- Описание функции и ее тип.
- Вызов функции.
- 5.2. Указатели в параметрах функций Указатель-параметр.
- Имитация подпрограмм.
- 5.3. Массивы и строки как параметры функций Массивы в параметрах.
- Резюме по строкам-параметрам.
- 5.4. Указатели на функции Указатели при вызове функций.
- Указатели на функции как параметры
- Указатель на функцию как возвращаемое функцией значение.
- Библиотечные функции с указателями на функции в параметрах.
- 5.5. Функции с переменным количеством параметров
- Доступ к адресам параметров из списка.
- Макросредства для переменного числа параметров.
- Примеры функций с переменным количеством параметров.
- 5.6. Рекурсивные функции
- 5.7. Классы памяти и организация программ Локализация объектов.
- Глобальные объекты.
- Динамическая память
- Внешние объекты.
- 5.8. Параметры функции main( )
- Глава 6. Структуры и объединения
- 6.1. Структурные типы и структуры Производные типы.
- Структурный тип.
- Определение структур.
- Выделение памяти для структур.
- Инициализация и присваивание структур.
- Доступ к элементам структур.
- 6.2. Структуры, массивы и указатели Массивы и структуры в качестве элементов структур.
- Массивы структур.
- Указатели на структуры.
- Указатели как средство доступа к компонентам структур.
- Указатели на структуры как компоненты структур.
- 6.3. Структуры и функции
- Имитация абстрактных типов данных.
- 6.4. Динамические информационные структуры Статическое и динамическое представление данных.
- Односвязный список.
- Рекурсия при обработке списка.
- 6.5. Объединения и битовые поля Объединения.
- Битовые поля.
- Глава 7. Ввод и вывод
- 7.1. Потоковый ввод-вывод
- 7.1.1. Открытие и закрытие потока
- 7.1.2. Стандартные файлы и функции для работы с ними
- Ввод-вывод отдельных символов.
- Ввод-вывод строк.
- Форматный ввод-вывод.
- Спецификаторы форматной строки для функции форматного вывода
- Спецификаторы форматной строки для функции форматного ввода
- 7.1.3. Работа с файлами на диске
- Двоичный (бинарный) режим обмена с файлами.
- Строковый обмен с файлами.
- Позиционирование в потоке.
- Трехъязычный словарь "Цифры
- 7.2. Ввод-вывод нижнего уровня
- 7.2.1. Открытие / закрытие файла
- 7.2.2. Чтение и запись данных
- 7.2.3. Произвольный доступ к файлу
- Глава 8. Примеры разработки программ
- 8.1. Программа с объектами разных классов памяти Постановка задачи.
- Программная реализация.
- 8.2. Структуры и обработка списков в основной памяти Постановка задачи.
- Функция main( ).
- Функция init( ) - "Инициализировать базу данных".
- Функция delete() - "Удалить все сведения о сотруднике из базы данных".
- Функция fr( ) - "Возвратить освобожденный элемент в список свободных элементов".
- Функция input( ) - "Ввести в базу данных сведения о новом сотруднике".
- Функция print( ) - "Печать списка занятых элементов".
- Сохранение (восстановление) базы данных.
- 8.3. Сортировка на основе бинарного дерева Статические и динамические данные.
- Управление динамической памятью.
- Сортировка с помощью бинарного дерева.
- Печать результатов сортировки.
- Раздел 2. Выполнение программ в разных операционных системах Глава 9. Подготовка и выполнение программ
- 9.1. Подготовка программ в операционной системе unix
- 9.1.1. Команда make
- Формат файла описаний зависимостей модулей.
- Формат команды make.
- Макроопределения.
- Встроенные правила.
- 9.1.2. Библиотеки объектных модулей
- Стандартные библиотеки.
- Создание и сопровождение собственных библиотек.
- 9.2. Сборка и выполнение программ в интегрированной среде Turbo с 2.0
- 9.2.1. Состав системы программирования Turbo с 2.0
- 9.2.2. Экран интегрированной среды Turbo с 2.0
- 9.2.3. Система меню среды Turbo с 2.0
- 9.2.4. Настройка среды Turbo с
- Создание рабочего каталога.
- Установка в среде Turbo с 2.0 полных имен каталогов.
- Настройка параметров управления проектом.
- 9.5. Окно определения проекта
- Сборка и выполнение программы.
- 1. Команды управления курсором:
- 2. Команды вставки и удаления:
- 3. Команды обработки блоков текста:
- 4. Дополнительные команды:
- 9.3.2. Экран интегрированной среды
- 9.3.3. Система меню интегрированной среды
- Задание полных имен основных и рабочего каталогов.
- Выбор стандарта языка Си.
- Установка параметров подсистемы Make.
- Создание проекта.
- Задание аргументов командной строки.
- Сохранение параметров настройки интегрированной среды.
- Сборка и выполнение программы.
- Работа в интегрированной среде в последующих сеансах.
- Раздел 3. Практикум по программированию на языке Си Глава 10. Задачи по программированию
- 10.1. Ознакомительная работа
- 10.2. Итерационные методы и ряды
- Варианты заданий по итерационным методам и рядам
- 10.3. Работа со строками. Указатели, динамические одномерные массивы
- 10..1. Варианты задач по обработке строк*
- 10.3.2. Рекомендации по обработке строк
- 10.3.3. Пример выполнения задания по обработке строк
- 10.4. Многомерные динамические массивы с переменными размерами
- 10.4.1. Варианты задач для 1-й части задания по многомерным массивам (правила формирования многомерного массива)
- 10.4.2. Варианты для 2-й части задания по многомерным массивам
- 10.4.3. Пример выполнения задания по многомерным динамическим массивам
- 10.5. Функции и указатели
- 10.6. Функции и массивы
- 10.7. Работа со структурами
- 10.7.1. Варианты структур для выполнения работы
- 10.8. Списки и деревья
- 10.8.1. Списки
- 10.8.2. Деревья
- Приложение 1. Таблицы кодов ascii
- Коды управляющих символов (0 31)
- Символы с кодами 32 127
- Символы с кодами 128 255 (Кодовая таблица 866 - ms-dos)
- Символы с кодами 128 255 (Кодовая таблица 1251 - ms Windows)
- Приложение 2. Константы предельных значений
- Приложение 3. Стандартная библиотека функций языка Си
- Функции для работы с терминалом в текстовом режиме (файл conio.H)
- Специальные функции
- Литература
- Содержание
- Раздел 1. Полный курс программирования на стандартном языке Си 4
- Глава 1. Базовые понятия языка 4
- Глава 2. Введение в программирование на языке си 33
- Глава 3. Препроцессорные средства 73
- Глава 4. Указатели, массивы, строки 91
- Глава 5. Функции 114
- Глава 6. Структуры и объединения 155
- Глава 7. Ввод и вывод 186
- Глава 8. Примеры разработки программ 218
- Раздел 2. Выполнение программ в разных операционных системах 256
- Глава 9. Подготовка и выполнение программ 256
- Раздел 3. Практикум по программированию на языке Си 282
- Глава 10. Задачи по программированию 282
- Подбельский Вадим Валерьевич Фомин Сергей Сергеевич программирование на языке си
- 101000, Москва, ул. Покровка, 7 Телефон (095) 925-35-02, факс (095) 925-09-57