Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгор_ТХТК_пособие.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.6 Mб
Скачать

I, k : Integer;

a, b:Char;

code : Word;

Begin

Assign(f, 'Dat3'); Assign(f1, 'Dat1');

Assign(f2, 'Dat2'); {Связали файловые переменные с физическими файлами на диске}

Reset(f);

Reset(f1);

Reset(f2); {Открыли файлы на чтение}

While Eof(f) = false do {Организуем цикл}

Begin

Read(f, a, b);{Считали из текстового файла два символа}

Val(b, k, code);{Превратили второй символ в ч исло}

If a = 'i' Then

Begin {Блок для работы с объектом типа "игрушка"}

New(lg);

Seek(f1, k); {Установили указатель файла на компонент к}

Read(f1, Ig^);

Writeln;

With Ig^ Do

Begin

Writeln('HanMeHOBaHne '.Name);

Writeln('Цена'.Cost);

Writeln('Hижняя возрастная граница ', Age1);

Writeln('Верхняя возрастная граница ', Age2);

End;

Dispose(lg);

End

Else

Begin

{Блок для работы с объектом типа "багаж"}

New(Ba);

Seek(f2, k);

Read(f2, ВаЛ);

With ВаЛ Do

Begin

Writeln('Количество ', Count);

Writeln('Bec ', W:8:2);

End;

Dispose(Ba);

End;

End;

Close(f);

Close(f1);

Close(f2);

End.

До тех пор, пока не будет достигнут конец файла f, считываются два символа а и b. Символ а указывает на то, с какой структурой придется работать. Символ b содержит номер компоненты типизированного файла. Перемещение на k-ую компоненту файла осуществляется с помощью функции Seek.

10.5 Списки

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

С писок - это набор записей, каждая из которых имеет поле данных и указатель (ссылку) на следующую запись в списке. Та, в свою очередь, тоже содержит поле данных и ссылку на продолжение списка. Последний элемент списка содержит зна­чение Nil, т.е. уже ни на что не ссылается. Начало списка формирует переменная типа "указатель", содержащая адрес первого элемента списка (рис. 10.4). Поле данных еще называют информационной частью списка.

Указатель в списке должен быть ти­пизированным. Базовым типом для него является тот же тип данных, что и тип ин­формационной части списка.

Рассмотрим методы работы со спис­ками, информационная часть которых со­стоит из одного поля типа Integer. Такие списки естественно называть списками целых чисел. Обозначим тип элемента списка идентификатором Elem, а ссылоч­ный тип на элемент списка – идентификатором

Рисунок 10.4 Линейный однонаправленный список

Type

Uk = ^Elem; {Описание типизированного указателя}

Elem = record{Описание базового типа}

х : Integer;

next : Uk;

End;

Var

p, q : Uk;

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

Построим список из трех элементов, содержащих числа 5, 12 и -8. Список обычно задается указателем на свой первый элемент. Назовем этот указатель р. Значением переменной р в процессе построения всегда будет ссылка на первый элемент уже построенной части списка. Переменная q будет использоваться для выделения памяти под размещение новых элементов списка. Выполнение оператора р := Nil приводит к созданию пустого списка. После выполнения операторов

New(q);

q^.x := -8;

q^.next := р;

р := q;

имеем список, состоящий из одного элемента, содержащего число -8 в информационной части. Ссылка на этот элемент является значением переменных р и q.

Выполнение операторов New(q); q^.x ;= 12; q^. next := p; p := q; приводит к тому, что в начало этого списка добавляется новый элемент, содержащий число 12; значением переменных р и q является ссылка на первый элемент списка.

После выполнения операторов New(q); q^.x := 5; q^.next := р; р := q, добавляющих в начало списка элемент, содержащий число 5, построение списка завершается.

Значением переменных р и q является ссылка на первый элемент списка. Значение Nil поля next элемента, содержащего число -8, является признаком того, что этот элемент - последний в списке.

Если значения -8, 12, 5 вводить с клавиатуры, то программа построения списка будет состоять из следующей последовательности операторов:

Туре

Uk = ^ Elem;

Elem = Record

х ; Integer;

next : Uk;

End;

Var

p, q : Uk;

i : Integer;

Begin

p := Nil;

For i := 3 downto 1 do

Begin

New(q); {Выделение памяти для элемента списка}

Readln(q^.x); {Ввод численного значения информационной части}

q^.next := p; {Запомнили адрес предыдущего элемента списка}

p:=q; {Запомнили адрес текущего элемента списка}

End;

End.

Заполнение списка начинается с конца списка.

Основными операциями над списками являются:

переход от элемента к следующему элементу;

включение нового элемента в список;

исключение элемента из списка.

Пусть значением переменной q типа ссылка является ссылка на элемент списка целых чисел, описанного выше. Тогда после присваивания q := q\ next ее значением будет или ссылка на следующий элемент этого списка (если такой элемент имеется) или Nil. Пользуясь таким способом перехода от одного элемента к другому, можно просматривать весь список или его часть. Для того, чтобы вывести числа, содержащиеся в элементах списка, нужно выполнить последовательность операторов

q := р;

While q <> Nil do

Begin

Write(q^.x); q := q^.next;

End;

По ходу выполнения цикла While q <> Nil do ... значением переменной q является поочередно ссылка на первый, второй, третий и т.д. элементы списка и, наконец, Nil.

Пусть имеются переменные р, q, r типа ссылка и значением переменной q является ссылка на некоторый элемент списка целых чисел, а значением р -ссылка на первый элемент этого списка. Требуется включить в данный список после элемента, ссылка на который является значением переменной q, новый элемент, содержащий число 17. Для этого нужно выполнить последователь­ность операторов:

New(r); r^.х := 17; r^.next := q^.next; q^.next := r;

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

r := q^.next; q^.next := q^. next^.next; r^.next := Nil;

Второе из этих присваиваний - исключение элемента из списка. Первое присваивание выполняется для того, чтобы сохранить ссылку на исключенный элемент. В этом случае элемент останется доступным и с ним можно выполнять нужные операции, например, освободить занимаемую им память с помощью Dispose. Третье присваивание выполняется для того, чтобы сделать исключение окончательным, т.е. чтобы из исключенного элемента по ссылке";} нельзя было попасть в список, из которого этот элемент исключен.

Процедура, исключающая из списка элемент, ссылка на который r:

Procedure exclude(Var p : Uk; r : Uk);

{р - указатель на первый элемент; r - указатель на исключаемый элемент}

Var q : Uk; {текущий указатель}

Begin

If p = г then p := г".next

else

Begin

q := p; While q^.next О г Do q := q^.next;

q^.next := r^.next;

End;

r := Nil;

End;

Для того чтобы включить в начало списка новый элемент, содержащий!) число 17, нужно выполнить действия:

New(r); r^.х := 17; r^,next := р; р := r;

Для исключения первого элемента из списка нужно выполнить:

r := р; р := p^.next; r^.next := Nil;

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

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

Рисунок 10.5 - Схематическое изображение очереди

Стек - линейный список, в котором добавления и исключения элемента производятся с одного конца, называемого вершиной стека (рис. 8). Соблюдается принцип "первым пришел - последним ушел".

Рисунок 10.6 - Стек

Считается, что доступ возможен только к верхнему элементу (в списках мы называли его первым). Стек оптимален для случаев, когда требуется просчитать и запомнить большое число структур данных, а потом обработать их в обратном порядке.

Работа со стеком осуществляется через указатель стека. При выполнении загрузки элемента в стек данные записываются на место, определяемое указателем стека, а указатель стека изменяет свое состояние и задает следующую свободную ячейку блока памяти. При извлечении элемента из стека указатель стека возвращается назад на один шаг. Добавить элемент в стек:

New(q); Readln(q^.x); q^.next := р; р := q;

Считать значение элемента из стека и исключить его из стека:

q := р; р := q^.next; у := q^.x; dispose(q);

Считать элемент, не удаляя его из стека:

y := Р^.х;

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

Мультисписки представляют собой структуру, каждый элемент которой входит в более чем один список одновременно и имеет соответствующее числу списков количество полей связи. Часто в виде мультисписков представляют матрицы очень большой размерности, в которых большинство элементов равны 0 (такие матрицы называются разреженными). Мультисписки • обеспечивают эффективное хранение таких структур в памяти: хранятся только те элементы, которые отличны от 0. Каждый элемент входит в два списка: в список-строку и список-столбец. Вся матрица представлена m + nсписками, где тип- соответственно число строк и число столбцов матрицы, каждый элемент хранит значение элемента матрицы и две ссылки: на следующий элемент в строке и на следующий элемент в столбце, указатели на начала этих списков хранятся в двух массивах. Описание типа данных одного элемента матрицы-мультисписка аналогично описанию элемента-очереди или узла дерева. Для описания матрицы потребуется два массива - массив указателей на первые элементы списков-строк и на первые элементы списков-столбцов.

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

Program Task1;

{Типизированная константа men содержит пункты меню основной части программы}

Const

men:array[1..8] of string[30] = ('Сформировать новый список','Добавить в список', 'Сформировать новую очередь','Добавить в очередь', 'Купить билет','Вывести список','Вывести очередь','Конец работы');

Type

Uk - ^Elem;

Elem = Record{Описание элемента'очереди)

Pred : Uk; Fam : String[10]; Punkt : String[10]; Date : String[5]; next : Uk;

End;

Sp = ^Elemi;

Elemi = Record{Описание элемента списка}

N : integer;

Punkt : String[10];

Date : String[5];

Count : Integer;

next : Sp;

End;

Var

o1, o2, o3 ; Uk;

s1, s2 : Sp;

{o1 - указатель на начало очереди, о2 - текущий указатель, оЗ - указатель на конец очереди, s1 - указатель на начало списка; s2 - текущий указатель}

a, i : integer;

Procedure Spisok;