Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
69
Добавлен:
10.02.2016
Размер:
640.51 Кб
Скачать

О.Н. Паулин. Основы теории алгоритмов

Глава 3

ПРЕДСТАВЛЕНИЕ данных В ПАМЯТИ ЭВМ

3.1. Понятие структуры данных

Издавна люди обрабатывали числовые данные о своей деятельности и об окружающем их мире, вовлечённом в эту деятельность. Уже к моменту появления ЭВМ (середина XX в.) процессы обработки данных при решении инженерных и научных задач были достаточно хорошо приспособлены для реализации на машине. С иным положением столкнулись экономисты, лингвисты и другие специалисты, для которых применение ЭВМ стало необходимостью. При постановке задач для решения её на ЭВМ потребовалось существенно повысить требования к обобщению представления данных. В ходе развития автоматизации программирования было установлено, что и сами программы следует рассматривать как объекты сложной структуры, подлежащие обработке на ЭВМ. Наконец, переход к интегрированной обработке данных в вычислительных системах и сетях значительно повысил интерес к методам размещения и поиска данных в памяти большого объёма.

В результате были определены объекты, отвечающие понятию струк-туры данных, и процессы, в которых эти объекты преобразуются; этим объектам были сопоставлены классы структур реальных данных. Так возникла прикладная дисциплина структуры данных. Она пользуется методами дискретной математики (теории множеств, математической логики, теории графов). Эти методы применяются для описания общих свойств и структур совокупностей данных. Такое описание строится, чтобы создавать эффективные алгоритмы обработки данных, упорядочивать сбор и хранение данных, и, в конечном счёте, оптимизировать потоки и ресурсы информации.

Правильный подход к разработке эффективного алгоритма для данной задачи - изучить её сущность. Часто задачу можно сформулировать на языке основных математических понятий, таких, как множество, и тогда алгоритм для неё можно изложить в терминах основных операций над основными объектами. Далее нужно проанализировать несколько структур данных и выбрать ту из них, которая лучше всего подходит для задачи в целом. Таким образом, разработка эффективного алгоритма предполагает выбор подходящей структуры данных. Не случайно возникла формула [9]:

Алгоритмы + структуры данных = программы

Информация, обрабатываемая вычислительной машиной при решении какой-либо задачи, состоит из некоторых данных о действительности. Данные являются абстракцией действительности, поскольку в них игнорируются некоторые свойства и характеристики реальных процессов, ситуаций либо объектов, несущественные для решаемой задачи. На основе этой абстракции строится математическая модель задачи (класса задач). Кроме того, данные организуются в определённые структуры (СД).

Итак, сначала надо выбрать (построить) модель и СД, описывающие решаемую задачу. Затем следует выбрать способ представления в памяти машины этой информации; здесь выбор предоставляется средствами, которыми обладает вычислительная машина, точнее, её программное обеспечение. В большинстве случаев эти два этапа взаимозависимы. Выбор представления данных в машине (машинных СД) часто бывает затруднителен, он не определяется однозначно имеющимися средствами. Его следует осуществлять с учётом операций, производимых над данными.

В привычной математической нотации используются числа, множества, последовательности и т. д., а не такие машинно-зависимые понятия, как последовательности разрядов. С другой стороны, неразумно писать программу для машины с помощью абстрактных нотаций, не затрагивая проблему представления. В качестве компромисса между этими край-ностями разработан машинно-независимый язык программирования Паскаль, для которого существует эффективный машинный транслятор. Именно этот язык используется в данном учебном пособии.

В программировании используется принцип, что каждая константа, переменная, выражение или функция бывает определённого типа. Этот тип существенным образом характеризует множество значений, которые являются константами, которые может принимать переменная либо выражение или которые может вырабатывать функция. Информацию о типах использует транслятор для проверки вычислимости и правильности различных конструкций языка.

Память вычислительной машины - это однородная совокупность разрядов без какой-либо структуры. Но именно абстрактная структура позволяет программисту определять типы данных в форме однообразных записей в ЭВМ.

В большинстве случаев новые типы данных определяются с помощью ранее определённых типов данных. Значения, принадлежащие к такому типу, обычно представляют собой совокупность значений компонент, принадлежащих к определённым ранее типам компонент; такие составные значения называются структурированными. Если имеется только один компонент, т. е. все компоненты принадлежат к одному типу, то он называется базовым.

Число различных значений, принадлежащих типу Т, называется кардинальным числом Т. Кардинальное число определяет размер памяти, необходимой для размещения в ней переменной x типа Т. Этот факт обозначается так: x:Т.

Большинство так называемых усложненных структур: последовательности, деревья, графы и т. д. - характеризуются тем, что их кардинальные числа бесконечны. Это отличие от структур с конечными кардинальными числами очень важно и имеет существенные практические следствия.

Последовательность с базовым типом Т0 - это любая пустая последовательность либо конкатенация последовательностей (с базовым типом Т0) и значения типа Т0.

Поскольку типы компонент также могут быть составными, можно построить целую иерархию структур, но конечные компоненты структур, разумеется, должны быть атомарными. Самый простой способ описания простого неструктурированного типа - это перечисление значений этого типа. Например, простой тип фигура может быть описан своими значениями: type фигура = (прямоугольник, квадрат, эллипс, круг). Но кроме типов, задаваемых программистом, нужно иметь стандартные типы, которые называются предопределёнными. Они обычно включают числа, логические переменные и множество символов печати.

Неструктурированный простой тип - множество, задаваемое перечислением его возможных значений. Стандартные простые типы - целые числа, вещественные числа, логические значения, множество символов печати, обозначаемые integer, real, boolean, char. Если значения некоторого типа упорядочены, то такой тип называется скалярным.

С учётом практических задач представления и использования данных универсальные языки программирования располагают определёнными методами структурирования. Эти методы позволяют строить структуры, описываемые как статические типы: массив, запись, множество и последовательность (файл). Более сложные структуры (списки, деревья, графы и т.д.) создаются во время выполнения программы, причём их размеры и вид могут изменяться; это динамические типы. Таким образом, различают статические и динамические структуры данных.

Массив - это наиболее широко известная структура данных; во многих языках это единственная структура, которая существует в явном виде. Массив - это регулярная структура: все его компоненты одного типа (базового). Массив - также структура с так называемым случайным (произвольным) доступом, все его компоненты выбираются произвольно и являются одинаково доступными.

Для обозначения отдельной компоненты к имени всего массива добавляется индекс, позволяющий выбирать компоненту. Индекс должен иметь значение типа, определённого как тип индекса массива. Описание регулярного типа Т задаёт таким образом не только базовый тип Т0, но и тип индексов l:

type T = array [l] of Т0

Приведём примеры описания регулярного типа:

type Row = array [1…5] of real

type Card = array [1…80] of char

Обычно считается, что массивы никак не упорядочены. Компоненты массива, в свою очередь, могут быть составными. Переменная-массив, компоненты которой являются массивами, называется матрицей. В качестве примера можно представить себе одномерный массив, компоненты которого являются векторами-столбцами одинаковой длины.

Записи. Самый общий метод получения составных типов - это объединение компонент, принадлежащих к произвольным, возможно, тоже составным типам, в один составной тип. Примеры из математики: комплексные числа, состоящие из двух вещественных чисел; координаты точек, состоящие из двух или более вещественных чисел, в зависимости от размерности пространства, заданного системой координат. Пример из обработки данных - это описание людей с помощью нескольких существенных характеристик, таких, как имя, фамилия, дата рождения, пол и семейное положение.

В теории множеств [5] такой составной тип называется декартовым произведением типов компонент. При этом число компонент составного типа равно произведению чисел компонент всех типов. В обработке данных комбинированные типы, такие, как описание людей или объектов, часто встречаются в файлах или "банках данных" и представляют собой записи существенных характеристик человека или объекта. Поэтому слово "запись" (record) стало широко принятым для обозначения подобной совокупности данных вместо термина "декартово произведение". Декартово произведение в принципе содержит все комбинации значений типов компонент, однако на практике не все такие комбинации могут быть "законными", т.е. иметь смысл.

В общем виде составной тип Т определяется следующим образом:

type T = record S1:T1

S2:T2

Sn:Tn

end.

Рассмотрим пример описания записей:

type Date = record day:1…31;

month:1…12;

year:1…2000

end.

Множество. Третья фундаментальная структура данных - множество (set). Значениями переменной x типа Т множества является совокупность элементов базового типа, Т0.

Его тип определяется таким образом: type T = set of T0.

Рассмотрим способы распределения (размещения) данных в оперативной памяти ЭВМ.

Оперативная память машины однородна и может быть представлена как линейка, состоящая из большого числа ячеек, в каждой из которых размещается одно данное. Доступ к некоторой ячейке для записи либо считывания данного осуществляется благодаря тому, что каждая ячейка имеет свой адрес (номер), явный или неявный; адресация ячеек, по умолчанию, сквозная. Для того, чтобы обеспечить эффективное использование памяти при наличии большого разнообразия математических моделей данных, надо так организовать данные (распределить их в памяти), чтобы был удобным доступ к нужной ячейке и чтобы основные операции над содержимым ячеек памяти: добавление (включение) или удаление (исключение) данного - не нарушали (не затрудняли) порядок следования данных.

Существуют два способа распределения данных в памяти ЭВМ: последовательное и связанное. В первом случае данные расположены в ячейках подряд (номера ячеек идут подряд), т. е. порядок следования элементов задаётся неявно требованием, чтобы смежные элементы последовательности находились в смежных ячейках памяти. Кроме того, имеет место фиксированное соотношение между номером i ячейки и элементом последовательности Si. Во втором случае данные связаны между собой специальными указателями, на которые отводится дополнительное место в ячейке; при этом каждому элементу Si последовательности данных поставлен в соответствие указатель Pi, отмечающий ячейку, в которой записаны элемент Si+1 и указатель на следующий за ним элемент.

Неудобство включения и исключения элементов при последовательном распределении происходит из-за того, что многие элементы последовательности во время включения или исключения должны передвигаться.

С помощью связанных распределений удалось добиться большей гибкости, потеряв некоторую информацию. Последовательное распределение позволяет иметь быстрый и прямой доступ к любому элементу последовательности. В связанном распределении доступ ко всем элементам последовательности, кроме первого, не является прямым и эффективным. Например, при последовательном представлении, если длина последовательности задана, то легко можно найти её срединный элемент. Это же самое труднее сделать, если последовательность задаётся связанным распределением; кроме того, приходится тратить память на указатели Pi.

Последовательное распределение наиболее естественно для статических структур данных, а связанное – для динамических.

При выборе последовательного или связанного представления разумно сначала проанализировать типы операций, которые будут выполняться над последовательностью. Если операции проводятся преимуще-ственно над случайными элементами, осуществляют поиск специфических элементов или проводят упорядочивание элементов, то обычно лучше последовательное распределение. Связанное распределение предпочтительнее, если в основном используются операции включения и/или исключения, а также сцепления и/или разбиения последовательности.

В языках программирования высокого уровня обязательно используются структуры данных, существенно более сложные, чем массивы. Ознакомимся с такими структурами данных, как списки, с помощью которых можно представлять множества, графы и деревья.

Соседние файлы в папке Основаная часть