Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
attachments_03-09-2012_10-20-12 / Связные списки,стеки, очереди.doc
Скачиваний:
32
Добавлен:
21.05.2015
Размер:
256 Кб
Скачать

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).

Для работы с очередью необходимы следующие операции:

  1. Инициализация очереди, т.е. подготовка структуры.

  2. Включение нового элемента в конец очереди.

  3. Проверка очереди на пустоту.

  4. Извлечение элемента из начала очереди.

При работе с очередью удобно хранить два указателя: указатель на начало очереди (голову) и указатель на последний элемент (хвост). Использование последнего указателя позволяет убрать последовательный перебор всех элементов из алгоритма добавления нового узла в конец очереди. Для определения очереди возьмем тип запись (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.