- •Предисловие
- •Основы программирования
- •Понятие алгоритма.
- •Алгоритм Евклида.
- •Задача о поездах и мухе
- •Вместо лирического отступления
- •Этапы подготовки задачи для решения на компьютере
- •Примеры разработки алгоритмов
- •Решение квадратного уравнения.
- •Вычисление интегралов
- •Обработка результатов эксперимента
- •Решение системы линейных алгебраических уравнений
- •Введение в язык программирования Pascal
- •Основные элементы языка
- •Переменные. Стандартные типы.
- •Операции отношения
- •Раздел описаний переменных
- •Выражения. Порядок выполнения операций.
- •Константы
- •Комментарии в программе
- •Операторы
- •2.1.7.1. Оператор присваивания
- •2.1.7.2. Операторы ввода/вывода
- •2.1.7.3. Операторы инкремента и декремента
- •Среда разработки Lazarus
- •Русский язык в консольных приложениях
- •Первая программа
- •Открытие существующего проекта
- •Другие способы создания консольных приложений
- •Типовой пустой проект
- •Операции с целыми числами
- •Вместо лирического отступления 2
- •Стандартные функции с целыми аргументами
- •Операции с вещественными числами (тип real).
- •Форматирование вывода
- •Одновременное использование вещественных и целых чисел.
- •Другие стандартные функции с вещественными аргументами
- •Булевы переменные
- •Условные операторы.
- •2.1.22.1 Оператор if …. then
- •2.1.22.2. Оператор if …then ... else
- •Операторы цикла
- •2.1.23.1. Оператор цикла с предусловием
- •2.1.23.2. Оператор цикла с постусловием
- •2.1.23.3. Оператор цикла с параметром.
- •2.1.23.4. Второй вариант оператора цикла с параметром
- •Оператор выбора case
- •Организация простейшего контроля ввода данных.
- •Вычисление сумм сходящихся рядов
- •Реализация некоторых алгоритмов главы 1.
- •Программа решения задачи о поездах и мухе
- •Программа вычисления определенного интеграла
- •Более сложные элементы языка
- •Общая структура Паскаль – программы
- •Процедуры и функции
- •3.1.1.1 Структура процедуры
- •3.1.1.2. Структура функции
- •3.1.1.3 Глобальные и локальные переменные
- •3.1.1.4 Способы передачи параметров
- •3.1.1.5 Процедуры завершения
- •Еще раз о типах данных
- •Классификация типов данных
- •3.2.1.1 Целый тип
- •3.2.1.2. Интервальный тип
- •3.2.1.3. Перечислимый тип
- •3.2.1.4. Множества
- •3.2.1.5. Логический тип
- •3.2.1.6. Вещественный тип
- •3.2.1.7. Указатели
- •Обработка символьной информации в Паскале
- •Символьные и строковые типы данных.
- •3.3.1.1. Тип Char
- •3.3.1.2. Функции для работы с символами
- •3.3.1.3. Тип String
- •3.3.1.4. Строковые процедуры и функции
- •Массивы
- •Динамические массивы
- •Программа решения системы линейных алгебраических уравнений методом Гаусса
- •3.4.1.1. Вариант 1 – с goto
- •3.4.1.2. Вариант 2 – без goto
- •3.4.1.3. Вариант 3 – наилучшая реализация
- •Модули в Паскале
- •Структура модуля
- •Системные модули
- •3.5.2.1. Модуль CRT
- •Файлы
- •Тип данных – запись
- •Файловые типы
- •Процедуры для работы с файлами
- •3.6.3.1. Общие процедуры для работы с файлами всех типов
- •3.6.3.2. Процедуры для работы с текстовыми файлами
- •3.6.3.3. Процедуры для работы с типизированными файлами
- •3.6.3.4. Процедуры для работы с нетипизированными файлами
- •3.6.3.5. Организация контроля ввода/вывода при работе файлами
- •3.6.3.6. Создание простой базы данных с типизированными файлами.
- •Алгоритмы сортировки
- •Обменная сортировка (метод "пузырька")
- •Сортировка выбором
- •Сортировка вставками
- •Метод быстрой сортировки
- •Алгоритмы поиска
- •Поиск в массивах
- •Вставка и удаление элементов в упорядоченном массиве
- •Динамические структуры данных
- •Представление в памяти компьютера динамических структур.
- •Реализация стека с помощью массивов
- •Указатели
- •Стандартные операции с линейными списками
- •Реализация динамических структур линейными списками
- •4.3.6.1. Реализация стека
- •4.3.6.2. Реализация очереди с помощью линейного списка
- •4.3.6.3. Реализация двоичного дерева с помощью линейного списка
- •Сортировка и поиск с помощью двоичного дерева
- •Три источника и три составные части ООП.
- •Классы и объекты.
- •Обращение к членам класса.
- •Инкапсуляция
- •Спецификаторы доступа.
- •Свойства.
- •Наследование
- •Полиморфизм
- •Раннее связывание.
- •Позднее связывание.
- •Конструкторы и деструкторы.
- •Элементы графического интерфейса
- •Различия между консольными и графическими приложениями
- •Визуальное программирование в среде Lazarus
- •Создание графического приложения
- •Форма и ее основные свойства
- •Компоненты
- •Обработчики событий
- •Простейшие компоненты
- •6.3.5.1. Компонент TLabel
- •6.3.5.2. Кнопки TButton, TBitBtn и TSpeedButton
- •6.3.6.1. Компонент TEdit
- •6.3.6.2. Компонент TLabeledEdit
- •6.3.7.1. Компонент TMaskEdit
- •Специальные компоненты для ввода чисел
- •Тестирование и отладка программы
- •Компоненты отображения и выбора данных
- •6.3.10.1. Компонент TMemo
- •6.3.10.2. Компонент TStringGrid
- •6.3.10.3. Компоненты выбора
- •Компонент TListBox
- •Компонент TComboBox
- •Компоненты выбора – переключатели
- •6.3.10.4. Компоненты отображения структурированных данных
- •Компонент TTreeView
- •Компонент TListView
- •Организация меню. Механизм действий - Actions
- •6.3.11.1. Компонент TMainMenu
- •6.3.11.2. Компонент TToolBar
- •6.3.11.3. Компонент TActionList
- •6.3.11.4. Создание приложений с изменяемыми размерами окон
- •Послесловие
- •Литература
- •Алфавитный указатель
Глава 4 Типовые алгоритмы обработки информации
____________________________________________________________________
стью, в реальных задачах деревья никто и никогда не вводит. Они формируют-
ся программно в зависимости от задачи. Пример автоматического создания де-
рева мы рассмотрим в разделе сортировка и поиск с помощью двоичного дере-
ва.
Теперь попробуйте самостоятельно реализовать другие способы обхода двоичного дерева, т.е. обход сверху и снизу.
Достоинства представления деревьев в виде массивов – относительная простота реализации. Для небольших деревьев, особенно если количество вер-
шин заранее известно, использование массивов может оказаться более эффек-
тивным решением. Но отсюда вытекает и недостаток, если количество вершин дерева заранее неизвестно, то, как и в случае со стеком, приходится резервиро-
вать память по максимуму. А каков этот максимум? Определенных критериев нет. Память расходуется непродуктивно. Еще один существенный недостаток – порядок расположения поддеревьев в массиве не поддается формализации.
Паскаль предоставляет значительно более удобный механизм для пред-
ставления списков и, следовательно, для реализации стека и деревьев. Рассмот-
рим этот механизм.
4.3.4 Указатели
Существует ряд задач, где статические структуры данных неэффективны для реализации алгоритма, поэтому в Паскале предусмотрена возможность ра-
боты с динамическими типами структур. С некоторыми из них, в частности стеками и связанными списками, мы познакомились в предыдущих разделах.
Также мы увидели, что стек можно реализовать, используя массивы.
Эффективным средством построения связанных списков являются указа-
тели.
Об указателях мы уже вели разговор в разделе 3.2.1.6, где шла речь о типе данных – указатель.
361
4.3 Динамические структуры данных
____________________________________________________________________
Напомним синтаксис объявления типизированных указателей:
type
Pint = ^integer;
var |
|
p: ^integer; |
{указатель на переменную целого типа} |
p1: ^ string; |
{указатель на строку символов} |
p2: Pint; |
|
Нетипизированный указатель описывается следующим образом:
var
ptr: pointer; {нетипизированный указатель}
Описание указателей p и p2 эквивалентны, т.е. можно применять тот и другой способ.
Указатель фактически является адресом памяти, по которому осуществля-
ется доступ к значениям динамической переменной.
Над указателями допустимы операции проверки на равенство (=) и нера-
венство (<>), а также возможно выполнение оператора присваивания := . Если список пуст, то значение переменной-указателя равно nil.
Память для динамических структур, реализуемых при помощи указателей,
распределяется в специальной области оперативной памяти компьютера и на-
зывается "кучей" (от англ. heap – куча) или динамически распределяемой памя-
тью (ДРП).
Для работы с динамическими переменными в Паскале предусмотрены процедуры:
New(p) – выделить в динамически распределяемой памяти (ДРП) необхо-
димую память для переменной заданного типа с типизированным указателем p;
362
Глава 4 Типовые алгоритмы обработки информации
____________________________________________________________________
Dispose(p) – освободить участок ДРП, занятый переменной с типизиро-
ванным указателем р.
Getmem(p, size) – второй способ выделить память переменной с указа-
телем p размером size байтов. Этой процедурой можно выделить память как для типизированных, так и для нетипизированных указателей.
Freemem(p, size) ) – освободить память распределенной переменной с указателем p размером size байтов.
Пример:
New(p);{Выделяется память для переменной целого типа} New(p1); {Выделяется память для строки символов}
Getmem(p2, 1000); {Выделяется память размером 1000 байт для пе-
ременной с указателем p2}
Как вы видите, динамические переменные не имеют собственного имени и доступ к значению такой переменной осуществляется через операцию разыме-
нования указателя. Для этого используется тот же символ "^", что использовал-
ся при описании указателей.
Пример:
p^:= 25;
p1^:= 'Это строка символов';
p2^:= 44;
При выделении памяти процедурой Getmem для типизированных указате-
лей и определении размера выделяемой памяти, лучше всего использовать про-
цедуру SizeOf так как, размеры памяти, отводимой под тот или иной тип, мо-
гут разниться в различных версиях компиляторов, например:
Getmem(p2, SizeOf(integer));
Обязательно освобождайте память после использования! Имейте в виду
363
4.3 Динамические структуры данных
____________________________________________________________________
только, что после освобождения памяти значения указателей не изменятся, что может привести к ошибкам. Необходимо присваивать значениям указателей nil после освобождения памяти. Кроме того, нельзя смешивать методы выде-
ления и освобождения памяти. Так, если память была выделена процедурой
New(), то нельзя освобождать память процедурой Freemem() и, наоборот,
если память была выделена процедурой Getmem(), то нельзя освобождать процедурой Dispose().
Теперь давайте построим связанный список, используя указатели. Для это-
го достаточно определить тип данных – запись и указатель на него. Структура записи будет следующей:
type
PMyList = ^TMyList; TMyList = record data: integer; next: PMyList; end;
var
mylist: PMyList;
В поле data находится содержательная часть элемента списка, причем data может быть, в свою очередь, записью достаточно сложной структуры.
Поле next – указатель. В нем содержится ссылка на следующий элемент спи-
ска. Благодаря этому указателю можно передвигаться к следующему элементу списка. В качестве признака окончания списка используют пустой указатель nil. Если необходимо двигаться по списку в прямом и обратном направлении,
то в структуру записи добавляют еще одно поле – указатель, в котором указы-
вают адрес предыдущего элемента списка. Такие списки с двумя указателями называются двусвязными или двунаправленными.
364