
- •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.3. Сбалансированные деревья
Бинарные деревья поиска предназначены для быстрого поиска данных. Время поиска определяется глубиной дерева. Для деревьев, у которых из каждой вершины выходит ровно две ветви, глубина дерева h = log2n-1, где n количество вершин дерева. Однако некоторые последовательности операций включения или исключения вершин могут привести к двоичным деревьям поиска, структура которых будет вырожденной: состоять из одного поддерева из n элементов. Например, для последовательности загружаемых в дерево поиска данных: 8,10, 14, 13 дерево поиска вырождается в линейный список. Максимальное количество сравнений для поиска вершины в таком дереве равно количеству элементов в дереве (n). Для того чтобы дерево использовалось для быстрого поиска необходимо после каждой вставки или удаления вершины выполнять перестроение дерева, чтобы его высота оставалась минимальной, а само дерево оставалось полным (сбалансированным).
Дерево считается идеально сбалансированным, если для каждой вершины дерева количества вершин в левом и правом поддеревьях различаются не более чем на 1. Пример идеально сбалансированного дерева поиска приведен на рис. 2.
Рис. 2. Идеально сбалансированное дерево
Идеально сбалансированные деревья поиска обеспечивают минимальное время поиска. Однако операции балансировки идеально сбалансированного дерева, которые необходимо выполнять при включении или исключении вершины, требуют больших временных затрат.
При частом включении и/ или исключении вершин поддерживать дерево идеально сбалансированным становится не выгодным из-за больших временных затрат на балансировку. Выходом из такой ситуации является компромисс – менее строгое определение сбалансированности.
Дерево называется сбалансированным, если высоты двух поддеревьев каждой вершины дерева отличаются не более чем на 1. Пример сбалансированного дерева приведен на рис. 3.
Рис.
3. Сбалансированное дерево
Это дерево не сбалансировано идеально, но высоты всех поддеревьев отличаются не более чем на 1.
Сбалансированные деревья называют АВЛ-деревьями по первым буквам фамилий авторов: Адельсон-Вельский Г.М. и Ландис Е.М. Согласно теореме, которую доказали Адельсон-Вельский Г.М. и Ландис Е.М. [3, 4], высота сбалансированного дерева никогда не будет превышать высоту идеально сбалансированного дерева более чем на 45% независимо от количества вершин. Например, для 1000000 вершин высота идеально сбалансированного дерева поиска равна 20, а для АВЛ-дерева – 29. При использовании АВЛ-деревьев время на поддержку баланса вершин дерева меньше, чем для идеально сбалансированных деревьев: в среднем балансировка требуется в одном случае из трех включений. Сбалансированные деревья поиска следует использовать тогда, когда поиск информации происходит гораздо чаще, чем включение и исключение данных.
10.2.4. В-деревья
Еще один способ уменьшения времени поиска в больших массивах данных – использование сильно ветвящихся деревьев. Примером использования сильно ветвящегося дерева может служить файловая система. Высота сильно ветвящегося дерева и, следовательно, количество сравнений при поиске в нем меньше, чем для бинарного дерева. Для m-арного дерева поиска загруженного n данными среднее количество сравнений для поиска logmn. Однако проблема балансировки существует и для сильноветвящихся деревьев поиска. Они также могут вырождаться в линейные списки.
В-дерево является особым видом сбалансированного сильно ветвящегося дерева. Оно используется в качестве индексного файла в базах данных. В-дерево 2-го порядка1 приведено на рис. 4.
Рис. 4. B-дерево порядка 2
В-деревом n-порядка называется сильно ветвящееся дерево поиска, обладающее следующими свойствами:
Листья дерева хранят либо настоящие записи файла данных, либо ключи и адреса записей файла данных. В первом случае В-дерево называется кластерным индексом, а во втором – некластерным индексом.
Все пути от корня дерева до любого листа имеют одинаковую длину: В-дерево сбалансировано по высоте. Сбалансированность В-дерева поддерживается при его эксплуатации.
Каждая внутренняя вершина содержит ключи данных и указатели на поддеревья. Ключи упорядочены по возрастанию. Каждый ключ имеет двух сыновей. В поддереве слева от ключа находятся вершины с меньшими значениями ключа, а в поддереве справа – с большими или равными значениями. Каждая вершина дерева n-порядка (кроме корня) содержит от n до 2n ключей. Количество указателей в вершине на 1 больше, чем количество ключей. Корень может содержать от 1 до 2n ключей. Для первой записи в любой внутренней вершине ключ не указывается. Это делается для экономии памяти и удобства включения.
Поиск в B-дереве – это прохождение от корня к листу в соответствии с заданным значением ключа. Так как дерево сбалансировано по высоте, то для выполнения поиска по любому значению ключа потребуется одно и то же (и обычно небольшое) число обменов с внешней памятью.