1. Концепция типа данных. Фундаментальные
структуры данных
Цель сегодняшнего занятия познакомится с
концепцией абстрактного типа данных и основными
структурами данных.
На сегодняшнем занятии мы обсудим основные
методы организации данных используемых в
компьютерных программах. Для многих задач выбор
подходящей структуры данных является самым
главным решением принимаемым в процессе их
реализации: как только выбор сделан, требуется
только применять очень простые алгоритмы. Для
одних и тех же данных, разные структуры требуют
большие или меньшие объемы памяти чем другие.
Некоторые структуры данных ведут к более
эффективным алгоритмам, а некоторые к менее. Этот
вопрос будет часто появляться на свет в ходе
изучения данного курса, так как выбор алгоритмов
и структур данных очень тесно связаны друг с
другом, и мы будем постоянно искать способы
экономии места или времени пытаясь сделать
наиболее подходящий выбор.
Структура данных это не пассивный объект: мы
должны также продумать множество операций
выполняемых над ней (и алгоритмы используемые в
этих операциях). Эта концепция формализуется
концепцией абстрактного типа данных, который мы
обсудим в конце этого занятия. Но нашим островным
интересом является конкретная реализация, и
поэтому мы сфокусируем наше внимание главным
образом на некоторыми специфичными
представлениями этих структур и их
манипулированием.
Мы будем иметь дело с массивами, связанными
списками, стеками, очередями и другими
простейшими типами данных. Это классические,
широко распространенные, структуры данных: они,
вместе с деревьями, формируют базис практически
для всех алгоритмов с которыми мы будем иметь
дело. В этом курсе, мы сосредоточим свое внимание
над основными способами их представления и
фундаментальными методами манипулирующими
этими структурами, разберем некоторые наиболее
специфические примеры их использования, и
обсудим некоторые связанные с этим вопросы
такие, как выделение памяти.
Тип данных определяет:
- Формат представления в памяти компьютера
- Множество допустимых значений, которые может
принимать принадлежащая к выбранному типу
переменная или константа
- Множество допустимых операций, применимых к
этому типу.
Основные (интересующие нас) типы данных в BP:
- Простые типы
- Целочисленные типы
- Логический тип
- Символьный тип
- Перечисляемый тип
- Интервальный тип
- Вещественный тип
- Структурированные типы
- Массив
- Множество
- Запись
1. Простые типы
1.1 Целочисленные типы
В Borland Pascal имеется пять предопределенных
целочисленных типов: Shortint, Integer, Longint, Byte и Word.
Каждый тип обозначает определенное подмножество
целых чисел.
Предопределенные целочисленные типы
Тип |
Диапазон |
Формат |
ShortInt |
-128...127 |
1 байт со знаком |
Integer |
-32768...32767 |
2 байта со знаком |
LongInt |
-2147483648...214748367 |
4 байта со знаком |
Byte |
0...255 |
1 байт без знака |
Word |
0...65535 |
2 байта без знака |
Значение одного целочисленного типа
может быть явным образом преобразовано к другому
целочисленному типу с помощью приведения типов.
1.2 Логический тип
Существует 4 предопределенных логических
(булевских) типа: Boolean, ByteBool, WordBool и LongBool. Значения
булевского типа обозначаются встроенными
идентификаторами констант False и True. Логические
переменные могут использоваться для хранения
результатов каких - либо логических вычислений.
Для булевых переменных разрешены только 2
операции сравнения "="(равно) и
"<>"(неравно).
1.3 Символьный тип (char)
Множеством значений этого типа являются
символы, упорядоченные в соответствии с
расширенным набором символов кода ASCII. Это буквы
['A'...'Z', 'a'...'z'], цифры ['0'...'9'], знаки препинания и
специальные символы. Переменная типа Char в памяти
занимает один байт.
1.4 Перечисляемый тип
Перечислимые типы определяют упорядоченные
множества значений через перечисление
идентификаторов, которые обозначают эти
значения. Упорядочение множеств выполняется в
соответствии с последовательностью, в которой
перечисляются идентификаторы.
Program Week;
Type
Week = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday,
Sunday);
Var
Day: Week;
Begin
Day:=Thursday;
If (Day = Sunday) Or (Day = Saturday) then
Writeln('Выходной')
Else
Writeln('Рабочий день');
End.
1.5 Интервальный тип
Интервальный тип представляет собой диапазон
значений из порядкового типа. Определение
интервального типа включает наименьшее и
наибольшее значение в поддиапазоне.
const
X = 50;
Y = 10;
Type
Interval = 0 ... 1000;
Scale = 2 * (X - Y)..(X + Y);
Такая декларация типа указывает компилятору,
что для переменных этого типа допустимы только
числа из указанного диапазона. Тем самым в
программе могут быть автоматически организованы
проверки корректности операций присвоения для
этих переменных. При выполнении программы
попытка присвоить такой переменной значение, не
входящее в интервал допустимых значений,
квалифицируется как ошибка и приводит к
прекращению работы программы и выдаче
соответствующего сообщения.
1.6 Вещественный тип.
К вещественному типу относится подмножество
вещественных чисел, которые могут быть
представлены в формате с плавающей точкой с
фиксированным числом цифр. Запись значения в
формате с плавающей запятой обычно включает три
значения - m, b и e - таким образом, что m * b ^ e = n, где b
всегда равен 2, а m и e являются целочисленными
значениями в диапазоне вещественного типа. Эти
значения m и e далее определяют диапазон
представления и точность вещественного типа.
Пример:
0.143E+22, где m - 0.143; b=2(подразумевается), e=22
Имеется пять видов вещественных типов:
вещественное (Real), с одинарной точностью (Single), с
двойной точностью (Double), с повышенной точностью
(Extended) и сложное (Comp).
Диапазон представления для
вещественных типов
Тип |
Допустимые значения |
Точность |
Формат |
Real |
2.9*10^-39...1.7*10^+38 |
11-12 знаков |
6 байт |
Single |
1.5*10^-45...3.4*10^+38 |
7-8 знаков |
4 байта |
Double |
5.0*10^-324...1.7*10^+308 |
15-16 знаков |
8 байт |
Extended |
3,4*10^-4932...1.1*10^4932 |
19-20 знаков |
10 байт |
Comp |
-9,2*10^+18...9.2*10^18 |
19-20 знаков |
8 байт |
2. Структурированные типы
2.1 Массив
Массив - это фиксированное количество
элементов данных, которые хранятся
последовательно и доступны по индексу.
Пример:
Type
Arr1= array[1..100] of Real;
Arr2 = array[boolean,1..10,Size] of Real;
Arr3 = array[1..10,1..8] of Boolean;
К i-му элементу массива a, идет обращение как к a[i].
На совести программиста остается обязанность
побеспокоиться о том, чтобы перед первым чтением
любого элемента массива в нем находилось
что-либо имеющее смысл.
Процесс предварительного предания переменным
"значащего" значения называется
инициализацией.
Пренебрежение инициализацией является
наиболее распространенной ошибкой при написании
программ.
Напишем программу, которая выводит простые
числа меньшие 1000.
Метод использованный в этой программе
называется "решето Эратосфена" и был
впервые применен уже 300 лет до рождества
Христова:
program Eratosfen;
const
N=1000;
var
a : array [1..N] of boolean;
i, j : integer;
begin
a[1] := false;
for i:=2 to N do
a[i] := true;
for i:= 2 to N div 2 do
for j:= 2 to N div i do
a[i*j] := false;
for i:= 1 to N do
if a[i] then
writeln(i);
end.
Программа использует массив состоящий из
элементов самого простого типа, логического.
Цель программы - присвоить элементу массива a[i]
значение true если i простое и false в противном
случае. Это достигается посредством того, что для
любого i, мы устанавливаем элементы массива с
индексами кратными i в значение false потому, что
любое число кратное какому либо целому не может
быть простым. Затем программа еще раз проходит по
массиву распечатывая простые числа.
Заметьте, что массив был изначально
проинициализирован, чтобы показать, что ни одно
из чисел более 1 не известно как "непростое":
алгоритм устанавливает в false лишь те элементы
массива a, индексы которых были опознаны как
непростые числа.
Решето Эратосфена является типичным
алгоритмом использующим факт, что любой элемент
массива можно эффективно использовать. Кроме
того, алгоритм использует элементы массива
последовательно, один за другим. Во многих
задачах, последовательный порядок доступа очень
важен; в некоторых других задачах
последовательный доступ используется потому,
что он столь же хорош, как и другие. Но самое
главное свойство массивов состоит в том, что если
индекс известен, то обращение к любому элементу
массива производится за постоянное время.
Размер массива должен быть известен заранее; в
Паскале, он должен быть известен уже во время
компиляции. Чтобы запускать вышеприведенную
программу при разных значениях N, необходимо
сперва изменить N, затем снова откомпилировать ее
и запустить. В некоторых программных оболочках
есть возможность объявлять размеры массива во
время выполнения программы (чтобы, например, дать
пользователю ввести значение N, а затем вывести
все простые числа меньшие либо равные N без
дополнительных затрат памяти на хранение
максимально большого массива), но и в этом случае
размер массива постоянен и должен быть известен
до того, как он первый раз использован.
Массивы относят к одним из фундаментальных
структур данных еще и потому, что существует
прямое соответствие между массивами и памятью на
практически всех типах компьютеров. Для
получения содержимого из памяти компьютера мы
должны знать адрес. Таким образом можно думать о
памяти компьютера как о массиве, с адресами
соответствующими индексам. Большинство языков
программирования преобразуют программы в
высокоэффективные программы в машинных кодах
которые используют память напрямую.
Другой знакомый нам всем способ
структурирования информации - это использование
двухмерных таблиц чисел организованных в
столбцы и колонки. Например, таблица содержащая
оценки студентов за домашние задания. На
компьютере такая таблица называется двухмерным
массивом. У нее два индекса, один для столбца,
другой для колонки. Алгоритмы определенные на
таких структурах очень просты: например, чтобы
подсчитать средний бал, мы суммируем все числа в
этом массиве и делим на количество элементов в
этом массиве, а чтобы подсчитать средний бал
одного студента, мы суммируем все его оценки и
делим на количество заданий. Двухмерные массивы
широко используются в задачах такого рода. На
самом же деле, на компьютере довольно удобно
использовать и более чем двухмерные массивы:
можно, например, использовать третье измерение
для обозначения года за который эта таблица
успеваемости.
Массивы также являются прямым прообразом
векторов. Аналогично, двухмерные массивы
соответствуют матрицам.
2.2 Множество
Множество - это набор элементов базового типа.
Переменная множественного типа может
принимать как все значения множества, так и ни
одного. Любой множественный тип может принимать
значение [], которое называется пустым
множеством.
Пример:
type
Digits = set of 0..9;
Letters = set of 'A'..'Z';
Операции над множествами
Операция |
Действие |
Результат |
+ |
Объединение |
Порядковое значение c содержится в a+b только
тогда, когда оно содержится в a или в b. |
- |
Разность |
Порядковое значение c содержится в a-b только
тогда, когда оно содержится в a и не содержится в b. |
* |
Пересечение |
Порядковое значение c содержится в a*b только
тогда, когда он содержится в обоих множествах a и
b. |
Если наименьшим порядковым значением, которое
является членом результата операций над
множествами, является a, а наибольшим - b, то типом
результата будет множество a..b.
Напишем программу использующею множества. На
примере программы реализующей детскую считалку.
program kinder;
const
nmax = 20;
var
n, {Число игроков}
m, {Число слов в считалке}
i, j: integer;
krug: set of 1..nmax;
begin
readln(n);
readln(m);
krug:=[1..n];{В круге стоит n детей}
i:=n;
repeat
for j:=1 to m do
repeat
i:= i mod n+1;
until i in krug;
writeln(i:3);
krug:=krug-[i];
until krug=[];
end.
2.3 Запись
Запись - это тип данных, позволяющий хранить
вместе переменные, имеющие различные типы
данных.
Пример:
Type
Date = record
Dd: 1..31;
Mm: 1..12;
Yy: integer;
End;
Var
D: date;
d.yy=1999;
d.mm:=06;
d.dd:=14;
Концепция определения типа данных в терминах
выполняемых над ними операций называется
концепцией абстрактного типа данных.
К примеру, множество поддерживает операции
объединения, разности, пересечения,
принадлежности элемента;массив поддерживает
операции присвоения и произвольного доступа до
элемента.
Основной идеей определения таких типов данных
является отделить "концепцию" того, чем
данная структура данных должна заниматься, от
какой-либо ее реализации.
Вы можете определить новый тип операции в
массиве например поиск максимального числа и
зная алгоритм нахождения максимального числа в
массиве Вы сможете реализовать его на любом
языке программирования, а определив эту операцию
в отдельный модуль Ваша программа сможет в
дальнейшем использовать эту операцию, а не
вдаваться в подробности связанные с циклами и
индексами массива.
Итак, сегодня мы познакомились с концепцией
абстрактного типа данных и основными
структурами данных. |