1.4 Типы данных, структуры данных и абстрактные типы данных
Рисунок 1.2 – Классификация структур данных
По характеру взаимосвязи структуры можно разделить на линейные - все элементы находятся на одном уровне, нелинейные - на нескольких уровнях. Для каждой разновидности типов структур данных характерны свои свойства и особенности в организации. В качестве общей характеристики выбрана запись. Запись - совокупность элементов о каком-то объекте. Они логически объединены в единую конструкцию, содержащую одно или несколько полей. Поле – минимальная единица данных, на которую можно ссылаться при обращении к данным. Одно из полей является ключевым и ключ содержит определенную величину, которую используют при упорядочении и поиске. Основной проблемой является выбор структуры данных и способа отображения в памяти зависящий от процедуры обработки данных.
Практически любую область математики ил других наук можно привлечь к построению определенного круга задач. И когда построена подходящая модель исходной задачи, то решение требуется искать в терминах этой модели. На этом этапе основная цель заключается в построении решения в форме алгоритма, состоящего из конечной последовательности инструкций, каждая из которых имеет четкий смысл и может быть выполнена конечными вычислительными затратами за конечное время. Инструкции могут выполняться в алгоритме любое число раз, при этом они сами определяют число повторений, однако программа, написанная на основе разработанного алгоритма, при любых начальных данных никогда не должна приводить к бесконечным циклическим вычислениям.
Хотя термины тип данных (или просто тип), структура данных и абстрактный тип данных похожи, они имеют различный смысл. В языках программирования низкого уровня (например, ассемблер) тип данных переменной обозначает множество значений, которые может принимать эта переменная (в языках высокого уровня любой встроенный в язык тип данных на самом деле ничем не отличается от создаваемых пользователем типов – классов). Например, переменная булевого (логического) типа может принимать только два значения: значение true (истина) и значение false (ложь). Набор базовых типов данных отличается в различных языках: в языке Pascal это типы целых (integer) и действительных (real) чисел, булев (boolean) тип и символьный (char) тип. Любой пользовательский тип данных может строиться на основе базовых типов.
Абстрактный тип данных – это математическая модель плюс различные операторы, определенные в рамках этой модели. Алгоритм может быть разработан в терминах АТД, но для реализации алгоритма в конкретном языке программирования необходимо найти способ представления АТД в терминах типов данных и операторов, поддерживаемых данным языком программирования. Для представления АТД используются структуры данных, которые представляют собой набор переменных, возможно, различных типов данных, объединенных определенным образом.
Базовым строительным блоком структуры данных является ячейка, которая предназначена для хранения значения определенного базового или составного типа данных. Структуры данных создаются путем задания имен совокупностям (агрегатам) ячеек и (необязательно) интерпретации значения некоторых ячеек как представителей (т.е. указателей) других ячеек.
В качестве простейшего механизма агрегирования ячеек в Pascal и большинстве других языков программирования можно применять (одномерный) массив, т.е. последовательность ячеек определенного типа. Массив также можно рассматривать как отображение множества индексов (таких как целые числа 1, 2, ..., п) в множество ячеек. Ссылка на ячейку обычно состоит из имени массива и значения из множества индексов данного массива. В Pascal множество индексов может быть нечислового типа, например (север, восток, юг, запад), или интервального типа (как 1..10). Значения всех ячеек массива должны иметь одинаковый тип данных. Объявление
имя: аrrау [ТипИндекса] оf ТипЯчеек;
задает имя для последовательности ячеек, тип для элементов множества индексов и тип содержимого ячеек.
Многие языки программирования позволяют использовать в качестве индексов только множества последовательных целых чисел. Например, чтобы в языке Fortran в качестве индексов массива можно было использовать буквы, надо все равно использовать целые индексы, заменяя "А" на 1, "В" на 2, и т.д.
Другим общим механизмом агрегирования ячеек в языках программирования является структура записи. Запись (record) можно рассматривать как ячейку, состоящую из нескольких других ячеек (называемых полями), значения в которых могут быть разных типов. Записи часто группируются в массивы; тип данных определяется совокупностью типов полей записи. Например, в Pascal объявление
var
reclist: array [1 .. 4] of record
data: real;
next: integer
end
задает имя reclist (список записей) четырехэлементного массива, значениями которого являются записи с двумя полями: data (данные) и next (следующий).
Третий метод агрегирования ячеек, который можно найти в Pascal и некоторых других языках программирования – это файл. Файл, как и одномерный массив, является последовательностью значений определенного типа. Однако файл не имеет индексов: его элементы доступны только в том порядке, в каком они были записаны в файл. В отличие от файла, массивы и записи являются структурами с «произвольным доступом», подразумевая под этим, что время доступа к компонентам массива или записи не зависит от значения индекса массива или указателя поля записи. Достоинство агрегирования с помощью файла (частично компенсирующее описанный недостаток) заключается в том, что файл не имеет ограничения на количество составляющих его элементов и это количество может изменяться во время выполнения программы.
В дополнение к средствам агрегирования ячеек в языках программирования можно использовать указатели и курсоры. Указатель (pointer) – это ячейка, чье значение указывает на другую ячейку. При графическом представлении структуры данных в виде схемы тот факт, что ячейка А является указателем на ячейку В, показывается с помощью стрелки от ячейки А к ячейке В.
В языке Pascal с помощью следующего объявления можно создать переменную-указатель prt, указывающую на ячейку определенного типа, например ТипЯчейки:
var
prt: ^ТипЯчейки
Постфикс в виде символа «^», в Pascal используется как оператор разыменования, т.е. выражение prt ^ обозначает значение (типа ТипЯчейки) в ячейке, указанной prt.
Курсор (cursor) – это ячейка с целочисленным значением, используемая для указания на массив. В качестве способа указания курсор работает так же, как и указатель «^», но курсор можно использовать и в языках (подобных Fortran), которые не имеют явного типа указателя. Интерпретировав целочисленную ячейку как индексное значение для массива, можно эффективно реализовать указания на ячейки массива. К сожалению, этот прием подходит только для ячеек массива и не позволяет организовать указание на ячейки, не являющиеся частью массива.
В схемах структур данных принято рисовать стрелку из ячейки курсора к ячейке, на которую указывает курсор. Иногда указывают целое число в ячейке курсора, напоминая тем самым, что это не настоящий указатель. Механизм указателя Pascal разрешает ячейкам массива только "быть указанными" с помощью курсора, но не быть истинным указателем. Другие языки программирования, подобные PL/1 или С, позволяют компонентам массивов быть истинными указателями и, конечно, "быть указанным" с помощью курсора. В отличие от этих языков, в языках Fortran и Algol, где нет типа указателя, можно использовать только курсоры.
Рисунок 1.3 – Пример составной структуры данных
На рис. 1.3 показана структура данных, состоящая из двух частей. Она имеет цепочку ячеек, содержащих курсоры для массива reclist (список записей), определенного выше. Назначение поля next (следующий) заключается в указании на следующую запись в массиве reclist. Например, reclist[4].next равно 1, поэтому запись 4 предшествует записи 1. Полагая первой запись 4, в соответствии со значениями поля next получим следующий порядок записей: 4, 1, 3, 2. Отметим, что значение поля next в записи 2, равное 0, указывает на то, что нет следующей записи. Целесообразно принять соглашение, что число 0 будет обозначать нуль-указатель при использовании курсоров и указателей. Но, чтобы не возникали проблемы при реализации этого соглашения, необходимо также условиться, что массивы, на которые указывают курсоры, индексируются начиная с 1, а не с 0.
Ячейки в цепочке на рис. 1.3 имеют тип
type
recordtype = record
cursor: integer;
prt: ^recordtype
end
На цепочку можно ссылаться с помощью переменной header (заголовок), имеющей тип recordtype, header указывает на анонимную запись типа recordtype.
Вообще, тип данных и класс являются синонимами. Класс инкапсулирует (encapsulates) информацию, связывая вместе члены и методы и обращаясь сними как с одним целым. Структура класса скрывает реализацию деталей и тщательно ограничивает внешний доступ как к дан ным, так и к операциям. Этот принцип, известный как скрытие информации (information hiding), защищает целостность данных.
Класс использует свои открытую и закрытую части для контроля за доступом клиентов к данным. Члены внутри закрытой части используются методами класса и изолированы от внешней среды. Данные обычно определяются в закрытой части класса для предотвращения нежелательного доступа клиента. Открытые члены взаимодействуют с внешней и могут использоваться клиентами.
В приложении доступ клиентов к открытым членам какого-либо объекта может быть реализован вне этого объекта. Доступом управляют главная про грамма и подпрограммы (master centrol modules), которые наблюдают за взаимодействием между объектами. Управляющий код руководит объектом для доступа к его данным путем использования одного из его методов или операций. Процесс управления деятельностью объектов называется передачей сообщений (message passing). Отправитель передает сообщение получающему объекту и указывает этому объекту выполнить некоторую задачу.
В нужный момент отправитель включает в сообщение информацию, которая используется получателем. Эта информация передается как данные ввода для операции. После выполнения задачи получатель может возвращать информацию отправителю (данные вывода) или передавать сообщения другим объектам, запрашивая выполнение дополнительных задач. Когда получающий объект выполняет операцию, он может обновлять некоторые из его собственных внутренних значений. В этом случае считается, что происходит изменение состояния (state change) объекта и возникают новые постусловия.
Абстрактный тип данных (АТД) представляет собой наиболее высокий из возможных уровень описания типов, создаваемых пользователем. В англоязычной литературе он обозначается как Abstract Data Type (ADT).
ADT НаименованиеАбстрактногоТипаДанных
Данные … перечисление данных, входящих в состав описываемого типа
Операции
Конструктор
Операция…
Операция …
Конец ADT НаименованиеАбстрактногоТипаДанных
|
Ниже представлен пример абстрактного типа данных «стек».
ADT Stack
Данные Список элементов с позицией top, указывающей на вершину стека.
Операции
Конструктор
StackEmpty
Pop
Push
Peek
ClearStack
Конец ADT Stack
|
- Министерство образования Российской Федерации
- Содержание
- 1.2 Скорость роста функций
- 1.3 Анализ алгоритмов; время работы в лучшем, худшем случаях и в среднем
- 1.4 Типы данных, структуры данных и абстрактные типы данных
- 1.5 Динамические множества
- 2 Алгоритмы сортировок
- 2.1 Понятие внутренней и внешней сортировки
- 2.2 Сортировка вставками
- 2.3 Сортировка слиянием
- 2.3.1 Описание алгоритма
- 2.3.2 Анализ времени работы алгоритмов «разделяй и властвуй»
- 2.3.2 Анализ времени работы сортировки слиянием через рекуррентное соотношение
- 2.3.3 Анализ времени работы сортировки слиянием через геометрическую интерпретацию
- 2.4 Пирамидальная сортировка
- 2.4.1 Введение в алгоритм
- 2.4.2 Сохранение основного свойства кучи
- 2.4.3 Построение кучи
- 2.5 Быстрая сортировка
- 2.5.1 Введение в алгоритм
- 2.5.2 Описание
- 2.5.3 Разбиение массива
- 2.5.4 Особенности работы быстрой сортировки
- 2.6 Особенности реализации алгоритмов сортировки; сортировка за линейное время
- 2.6.1 Введение
- 2.6.2 Разрешающее дерево сортировки сравнениями
- 2.7 Цифровая сортировка
- 2.8 Сортировка вычерпыванием
- 2.8.1 Описание алгоритма
- 2.8.2 Вероятностный анализ времени работы сортировки вычерпыванием
- 2.8.3 Анализ времени работы сортировки вычерпыванием через геометрическую интерпретацию
- 2.9 Сортировка подсчетом
- 2.9.1 Описание алгоритма
- 2.9.2 Анализ времени работы
- 3 Элементарные и нелинейные структуры данных
- 3.1 Элементарные структуры: список, стек, очередь, дек
- 3.1.1 Список Линейный однонаправленный список
- Линейный двунаправленный список
- Двунаправленный список с фиктивными элементами
- Циклические списки
- Циклический однонаправленный список
- Циклический двунаправленный список
- 3.1.2 Стек
- 3.1.3 Очередь
- 3.1.3 Дек
- 3.2 Нелинейные структуры данных
- 3.2.1 Представление корневых деревьев в эвм
- Обходы деревьев
- 3.2.2 Двоичные деревья Спецификация двоичных деревьев
- Реализация
- Обходы двоичных деревьев
- 3.2.3 Двоичные деревья поиска Основные операции
- Минимум и максимум
- Следующий и предыдущий элементы
- Добавление и удаление
- Случайные деревья поиска
- Оптимальные деревья поиска
- 4 Хеширование
- 4.1 Введение
- 4.2 Прямая адресация; таблицы с прямой адресацией
- 4.3 Хеш – таблицы; возникновение коллизий и их разрешение
- Разрешение коллизий с помощью цепочек
- Анализ хеширования с цепочками
- 4.4 Способы построения хеш – функций Выбор хорошей хеш-функции
- Ключи как натуральные числа
- Деление с остатком
- Умножение
- Универсальное хеширование
- 4.5 Открытая адресация; способы вычисления последовательности испробованных мест: линейная последовательность проб, квадратичная последовательность проб, двойное хеширование
- Линейная последовательность проб
- 1 / (1 – )
- 5 Основные принципы разработки алгоритмов
- 5.1 Введение в теорию графов
- 5.1.1 Графы
- 5.1.2 Представление графов
- 5.2 Алгоритмы на графах: поиск в ширину, поиск в глубину
- 5.2.1 Поиск в ширину (волновой алгоритм)
- 5.2.2 Анализ поиска в ширину
- 5.2.3 Деревья поиска в ширину
- 5.2.4 Поиск в глубину
- 5.2.5 Анализ поиска в глубину
- 5.2.6 Свойства поиска в глубину
- 5.2.7 Классификация рёбер
- 5.3 Топологическая сортировка, задача о разбиении графа на сильно связанные компоненты
- 5.3.1 Топологическая сортировка
- 5.3.2 Сильно связные компоненты
- 5.4 Алгоритм построения минимального остовного дерева
- 5.4.1 Остовные деревья минимальной стоимости
- 5.4.2 Построение минимального покрывающего дерева
- 5.4.3 Алгоритмы Крускала и Пpимa
- 5.4.4 Алгоритм Крускала
- 5.4.5 Алгоритм Прима
- 5.5 Задача нахождения кратчайших путей на графах; алгоритм Флойда; алгоритм Дейкстры
- 5.5.1 Нахождение кратчайшего пути
- 5.5.2 Алгоритм Дейкстры
- 5.5.3 Алгоритм Флойда
- 5.6 Поиск с возвратом
- 5.6.1 Введение
- 5.6.2 Переборные алгоритмы
- 5.6.3 Метод ветвей и границ
- 5.6.4 Метод альфа-бета отсечения
- 5.6.5 Локальные и глобальные оптимальные решения
- 5.7 Метод декомпозиции ( «Разделяй и властвуй»)
- 5.7.1 «Ханойские башни»
- 5.8 Жадные алгоритмы и динамическое программирование
- 5.8.1 Задача о выборе заявок
- 5.8.2 Дискретная задача о рюкзаке
- 5.8.3 Непрерывная задача о рюкзаке
- 5.8.4 Числа Фибоначчи
- 5.8.5 Задача триангуляции многоугольника
- 5.8.6 Дп, жадный алгоритм или что-то другое?