- •Оглавление
- •Связные списки, стеки и очереди
- •Односвязные списки
- •Узлы связного списка
- •Создание односвязного списка
- •Вставка и удаление элементов в односвязном списке
- •Прохождение связного списка
- •Создание упорядоченного списка.
- •Использование упорядоченного связного списка для частотного анализа данных
- •Interface
- •Implementation
- •Очереди
- •Задачи.
Interface
type
PElem = ^Elem;
Elem = record
Data : Integer;
Next : PElem;
end;
Procedure Push(var Top:PElem; N:integer);
Procedure Pop(var Top:PElem; var N:integer);
Function IsEmpty(Top:PElem):Boolean
Implementation
Procedure Push(var Top:PElem; N:integer);
Var Temp:Pelem;
begin
new(Temp); // выделяем память под новый элемент
Temp^.Data:=N; // записываем значение N в новый элемент
Temp^.Next:=Top; // связываем новый элемент с первым
Top:=Temp;// новое начало стека – первый элемент указывает на новый
end;
Procedure Pop(var Top:PElem; var N:integer);
Var Temp:Pelem;
begin
N:=Top^.Data; // считываем значение из первого элемента в N
Temp:=Top; // запоминаем адрес первого элемента
Top:=Top^.Next; // переставляем указатель на первый элемент на следующий
Dispose(temp); // удаляем бывший первый элемент
end;
Function IsEmpty(Top:PElem):Boolean
begin
Result:=Top=nil; //
end;
end. {Steck}
Продемонстрируем использование стека из модуля Steck на примере решения следующей задачи – в текстовом файле записаны целые числа, распечатать на экране все числа в обратном порядке. Из условия задачи видно, что количество чисел в файле заранее неизвестно, поэтому поместить их все в массив и распечатать содержимое массива обратном порядке проблематично (размер массива должен быть задан заранее). Прочитать текстовый файл в обратном порядке невозможно, так как для текстовых файлов определен только последовательный доступ к его элементам. Один из простых вариантов решения этой задачи будет последовательное чтение данных из файла и заталкивание их в стек, потом надо просто извлечь все данные из стека и вывести их на экран. Листинг программы приведен далее. Из модуля Steck в программе использованы процедуры и функции и описание указателя на элемент стека тип - PElem.
Листинг программы, использующей модуль Steck.
Program TestSteck;
{$APPTYPE CONSOLE}
Uses SysUtils, Steck; // подключение модулей
Var N:integer;
HeadSteck:PElem; // HeadSteck – указатель на первый элемент стека
FileName:string; // имя файла с данными
F:text; // файловая переменная
Count:integer; // счетчик считанных из файла чисел
begin
HeadSteck:=nil; // инициализация стека, стек – пуст
repeat
Write(‘Введите имя файла с данными’);
Readln(FileName);
until FileExists(FileName); // цикл с вводом имени файла будет продолжаться до тех пор,
//пока не будет введено имя существующего файла
Assign(F,FileName); // связываем файловую переменную f с именем файла FileName
Reset(F); // открываем файл для чтения
Count:=0;
While not Eof(F) do
begin
Read(F,N) ; // читаем из файла число в переменную N
Inc(Count);
Push(HeadSteck,N); // заталкиваем число N в стек
end;
Close(f);
Writeln(‘Всего считано и добавлено в стек ‘, Count, ‘ чисел);
While not IsEmpty(HeadStecK) do // пока стек не пуст, будем извлекать данные и печатать
begin
Pop(HeadStecK,N);
Writeln(N);
end;
Readln;
end.
Очереди
Следующей широко известной базовой структурой данных является очередь. В то время как извлечение элементов из стека происходит в порядке, обратном тому, в котором они вносились, в очереди элементы выбираются в порядке их добавления. Таким образом, очередь относится к структурам типа "первый пришел, первый вышел" (FIFO – first in, first out). С очередью связаны две основные операции: постановка в очередь (т.е. добавление нового элемента в очередь) и снятие с очереди (т.е. извлечение из нее самого старого элемента).
Иногда эти операции ошибочно называют заталкиванием и выталкиванием. Это абсолютно неверные термины для очереди. Ближе к истине будут слова включение и исключение.
Рис.7. Постановка в очередь и снятие с очереди
Как и стеки, очереди можно реализовать на основе односвязных списков или массивов. Тем не менее, в отличие от стеков, очень трудно добиться высокой эффективности реализации на основе массивов. Если в процессе работы очередь то очень длинная, то короткая, имеет смысл реализовать очередь с использованием динамической структуры. К тому же организация очередей на базе связных списков очень проста. Поэтому рассмотрим построение очереди на базе односвязных списков. Фактически мы должны смоделировать обычную очередь в универмаге. С помощью списков это можно сделать очень легко, поскольку сами списки по своей сути являются очередями. Просто для моделирования очереди элементы должны добавляться с одной стороны и удаляться с другой. При использовании односвязного списка снятие с очереди будет выполняться с начала списка, а постановка в очередь - в конец списка. Очевидно, что операции с очередью не зависят от количества элементов в ней, т.е. они принадлежат к классу О(1).
Для работы с очередью необходимы следующие операции:
Инициализация очереди, т.е. подготовка структуры.
Включение нового элемента в конец очереди.
Проверка очереди на пустоту.
Извлечение элемента из начала очереди.
При работе с очередью удобно хранить два указателя: указатель на начало очереди (голову) и указатель на последний элемент (хвост). Использование последнего указателя позволяет убрать последовательный перебор всех элементов из алгоритма добавления нового узла в конец очереди. Для определения очереди возьмем тип запись (Record) с именем Tqueue, содержащую в качестве полей два этих указателя (Head - начало, Tail - конец). В этом случае очередь будет характеризоваться только одним параметром.
Листинг динамической реализации очереди
type
PElem = ^Elem; // описание указателя на элемент очереди
Elem = record // описание элемента очереди
Data : Integer;
Next : PElem;
end;
Tqueue =record // определение очереди
Head, // голова очереди
Tail: Pelem; // хвост очереди
end;
procedure InitQueue(var q:TQueue); { инициализация очереди, указателю на начало очереди присвоить пустое значение}
begin
q.Head:=nil;
end;
function QueueIsEmpty(q:TQueue): Boolean; { проверка очереди на пустоту, очередь пуста, если указатель на начало равен nil}
begin
Result:=q.Head = nil;
end;
procedure InQueue(var q:TQueue; x:integer); // поставить в очередь
var P:Pelem;
begin
new(p); {создаем и заполняем значениями новый элемент очереди}
p^.Data:=x;
p^.next:=nil;
{Далее необходимо рассмотреть 2 ситуации:
а) очередь пуста - добавление первого элемента очереди
б) очередь не пуста - добавление нового элемента в конец }
if QueueIsEmpty(q) then // очередь пуста – создаем первый элемент
begin
q^.Head:=p; q^.Tail:=p;
end
else
With q do begin
Tail^.next:=p; Tail:=p; { присоединяем новый элемент к концу очереди, присваиваем указателю на конец очереди новое значение}
end;
end;
function OutQueue(var q:TQueue):integer; // взять из очереди
var P:Pelem;
begin
With q do begin
Result:=Head^.Data; // возвращаем значение поля Data
p:=Head; // запоминаем адрес начала очереди
Head:=Head^.Next; // переставляем начало очереди на следующий элемент
Dispose(p); // удаляем бывший первый элемент
end;
end;
ФункциюOutQueue(извлечь элемент из очереди) можно вызывать только в том случае, когда очередь не пустая. Поэтому перед извлечением очередного элемента необходимо проверять очередь на не пустоту, вызывая функцию not QueueIsEmpty.
Продемонстрируем использование очереди на решении следующей задачи. В текстовом файле записаны положительные и отрицательные числа. Вывести на экран сначала отрицательные, а затем положительные числа не изменив порядок их расположения. Выполнить данные действия необходимо за один проход по файлу.
С использованием очереди данная задача решается очень просто. Читаем число из файла, если оно положительное, то добавляем число в очередь, иначе выводим его на экран. После чтения всех чисел из файла, отрицательные выведены на экран, а положительные находятся в очереди. После этого необходимо извлечь все положительные числа из очереди и вывести их на экран
Листинг программы, использующей очередь.
Program ………..;
Type ….{ описание типов для реализации очереди}
…….
Var N:integer;
HeadQueue:TQueue; // HeadQueue – указатели на очередь
FileName:string; // имя файла с данными
F:text; // файловая переменная
begin
InitQueue(HeadQueue); // инициализация очереди
Write(‘Введите имя файла с данными’);
Readln(FileName);
Assign(F,FileName); // связываем файловую переменную f с именем файла FileName
Reset(F); // открываем файл для чтения
While not Eof(F) do
begin
Read(F,N) ; // читаем из файла число в переменную N
If N>=0 then InQueue(HeadQueue,N) // помещаем число N в очередь
else Writeln(N); // иначе выводим на экран
end;
Close(f);
While not QueueIsEmpty(HeadQueue) do // пока очередь не пуст, будем извлекать
// данные и печатать на экране
begin
Writeln(OutQueue(HeadQueue));
end;
Readln;
end.