
4) Стек на базі масиву
type
T = <опис типа Т>
stack_T = record
size: word
TopInd: word
Pbody: ^body;
end;
Body=array[1..65536 div SizeOf(T)] of T;
{stack_T=data type is Create,Push,Pop,IsEmpty,Done}
Опис: Stack_T - це змінні стеки елементів типу Т. Операції Push і Pop змінюють стан стека.
Procedure Create(Var S:Stack_T; size_stask:word);
Requires 0sizeStask65535 div sizeof(T)
Effects створюють порожній стек елементів типу Т з розміром в sizestask елементів типу Т
Procedure Push(Var S:Stask_T;X:T);
Requires S - містить менше елементів, чим S.size
Modifies S
Effects заштовхує елемент X в стек S
Procedure Pop (Var S:Stask_T);
Requires S – непорожній
Modifies S
Effectsвиштовхує елемент з вершини стека
Function Top (S:Stask_T):T;
RequiresS- непорожній
Effects повертає елемент, що знаходиться у вершині стеку.
Function IsEmpty(S:Stask_T):boolean;
Effects IsEmpty=S=порожній стек
Procedure Done (Var S:Stask_T);
Effects звільняє пам'ять, займану тілом стека, і приводить дескриптор у неробочий стан.
Implementation
{ Функція абстракції:
Типовий стек є послідовність елементів e1,e2,…,en, де n - вершина стека (тобто стек росте у напрямку старших індексів масиву PBody). }
{ Функція інваріант подання:
1 size 65535 div sizeof(T) & 0 TopInd size & PBody^[TopInd] - елемент у вершині стека. Pbody вказує на вектор пам'яті розміром size*sizeof(T) }
Procedure Create;
BEGIN
if sizeStaskSizeOf (T) > 65536 then Halt(1);
GetMem (S.Pbody^sizeStask SizeOf (T));
S.TopInd :=0;
S.Size := sizeStask;
END;
Procedure Push;
BEGIN
inc(S.TopInd);
S.Pbody^[S.TopInd] :=X;
END
Класичний приклад на використання стека:
Хай маємо ( (х+2)+3-3*(х+3) )*х
0 1 1 2 1 0
// Перевірити правильність розстановок дужок
Якщо дужки різного вигляду:
([х+2]+3-3*[х+3])*х
Використовуємо стек для зберігання дужок, що відкрилися. Якщо закриваємо дужки, то перевіряємо є на верх стека що така ж відкривається.
Черга елементів типу ℓ .
Додавання в Tail, вибірка з Head.
Необхідно реалізувати об'єкт типу черга.
type
TQueue=object
procedure create; методи (поведінка)
function Empty:boobcan;
procedure EnQ(U: E); // додати в хвіст
procedure DeQ(var U: E); // видалення з голови
function Head E;
function Tail E; // заглянути в чергу
.
.
.
procedure Down // зруйнувати чергу
end;
Існує два способи реалізації:
1.Обмежена черга на базі масиву;
2.Необмежена черга на базі списку.
Обмежена черга на базі масиву:
Head = <указує індекс елементу, звідки необхідно читати >
Tail = <указує на останній записаний елемент >
Ці покажчики будуть повзти по массиву, однак у процесі роботи виникає питання, що робити, коли хвістдоповзе до кінця масиву ?
Існує два способи:
Перемістити всю чергу на початок – ПОГАНО.
Наступний елемент помістити у початок – КРАЩЕ. Однак в цьому випадку стають не помітні два випадки:
Коли черга порожня: покажчик голови буде на 1 більше покажчика хвоста
Переповнення черги.
Необхідно продумати як виглядатиме порожня черга:
Приймемо, що порожня черга виглядає: Head=0
Tail=size -1
Розглянемо проміжний випадок:
Нехай:
Нехай я 1) ПИШУ, тоді Head залишиться незмінним, а
Tail необхідно просунути на 1.
2) ЧИТАЮ: Head зрушую на наступний, а Tail не
зміниться.
Уявимо, що я тільки ПИШУ, то Tail доповзе до кінця, тоді необхідно писати з початку і у нас змійка постійно бігатиме
Нехай:
Head=Tail+1 (ознака переповнювання)
Або випадок:
читаємо, читаємо, виходить
получаем
ознака порожнечі
Що робити, якщо Tail знаходиться в кінці. Для цього пропонують загнути чергу в бублик за допомогою операції:
Next(i)= i+1, якщо i<size-1
0 , якщо i>=size-1
Виникають проблеми з неоднозначністю. Подивимося на операції ПИСАТИ і ЧИТАТИ.
Коли я ПИШУ, тоді умову Next, проверяю перед операцією, а коли ЧИТАЮ - тоді після.
Для цього ми на підставі черги додаємо до опису булеве поле Empty.
Empty:
Запишемо алгоритм читання і запису на псевдокоді.
Пишу:
ЯКЩО Head=Next(Tail)
ТОДІ
ЯКЩО Empty тоді
Tail=Next(Tail) та
Пишу на місце Tail і скидаю прапор Empty // в false
ІНАКШЕ помилка переповненої черги
ВСЕ ЯКЩО
Читаю:
ЯКЩО Empty ТОДІ
Помилка - пустота черги
ІНАКШЕ
Читаю з місця Head
і просуваю Head = Next(Head)
ЯКЩО head=Next(Tail) ТОДІ
Empty = True
ВСЕ ЯКЩО
При роботі з чергою необхідно перевіряти чергу на виявлення наступних ситуацій:
Не допускати заповнення масиву до кінця.
Head<>Next(Tail)
Завести Спеціальний признак порожнечі черги. Перевіряти його перед вибіркою та встановлювати (якщо потрібно)після вибірки.
Замість того, щоб зберігати покажчик на хвіст і голову, то ми можемо зберігати покажчик на голову і кількість елементів - [size].
Недолік цього – немає явного покажчика на хвіст.
Перевага – просто розпізнаються операції переповнення та порожнечі черги.
Реалізація черги на базі списку
Виникає питання: Де зберігати Head і Tail ?
Ідея 1: Необхідно зберігати покажчик на голову і хвіст, тобто
Наприклад: необхідно читати
- запом’ятовуємо Head в P;
- виправляємо Head Next(Head);
- видати Dispose для цього вузла.
Якщо необхідно писати:
-видаляємо вузол і запам'ятовую в Р;
В цьому випадку виникають проблеми, наприклад: якщо черга порожня.
Для вирішення необхідно ввести фіктивний вузол, для того, щоб не допускати до порожнечі.
Ідея 2: Зберігати покажчик на хвіст
Tail указує на хвіст
Таким чином, в цьому випадку кільцевий список. Але тут теж виникають проблеми: переповнювання і спустошення.
Послідовність.
Послідовність - послідовний файл в оперативній пам'яті. Елементи послідовності в кожний момент часу розділені на дві частини – прочитану та не прочитану.
Після створення послідовності прочитана і непрочитана частина є порожніми. При додаванні елементу в послідовності він додається в кінець. При цьому непрочитана частина збільшується, а прочитана не змінюється. Запис в послідовність йде тільки в кінець. По операції “встати в початок послідовності” (Reset) прочитана частина стає порожньою, а непрочитана частина співпадає зі всією послідовністю. Непрочитана частина поводиться як черга.
Цю структуру можно реализовать на базі 2-х стеків:
Реализация последовательности на базе 2-х стеков:
Початкова ситуація:
0 0 0 0 0 0
Ситуація читання:
0 0
0 0 0 0 0 0
Читаю:
0 0 0
0 0 0 0 0 0
Реалізація об'єкту типу послідовність.
Type
SEQ =object
LeftQ, RightQ : QueneE;
procedure Create;
procedure ReWrite;
procedure Reset;
procedure Write(El:E);
procedure Read(var El:E);
procedure Skip;
function Empty:boolean;
function EQSeg: boolean;
function CurEl;
procedure Done;
end;
procedure SEQ.Create;
begin
LeftQ.Create;
Right.Create;
end;
procedure ReWrite;
var El: E;
begin
while not LeftQ.Empty do
LeftQ.DeQ(El);
while not RightQ.Empty do
RightQ.DeQ(El);
end;
procedure Reset;
begin
while not RightQ.Empty do
begin
RightQ.DeQ(El);
LeftQ.EnQ(El);
end;
while not LeftQ.Empty do
begin
LeftQ.DeQ(El);
RightQ.EnQ(El);
end;
end;
procedure Write(El:E);
begin
RightQ.EnQ(El);
end;
procedure Read(var El:E);
begin
RightQ.DeQ(El);
LeftQ.EnQ(El);
end;
procedure Skip;
var El: E;
begin
RightQ.DeQ(El);
LeftQ.EnQ(El);
end;
function Empty:boolean;
begin
Empty:=LeftQ.Empty and RightQ.Empty
end;
function EQSeg: boolean;
begin
EQSeg:= RightQ.Empty;
end;
function CurEl:E;
begin
CurEl:= RightQ.Head;
end;
procedure Done;
begin
LeftQ. Done;
RightQ.Done;
end;
Еластична стрічка.
Еластична стрічка - Структура з однією головкою читання-запису, яка може пересуватися за стрічкою з можливістю вставки/видалення елементу у(з) будь-якого місця стрічки, на яке вказує голівка. Стрічка може необмежено розтягуватися та стискуватися у місці де розташована голівка.
При читанні елементу він завжди видаляється зі стрічки.
Реализация об'єкта типу еластична стрічка.
Elastic=object
CurList:NotePtr;
EoList: NotePtr;
procedure Create;
function Empty:boolean;
procedure Write(El:E);
procedure Read(var El:E);
procedure SkipLeft;
procedure SkipRight;
function EOLeft: boolean;
function EORight: boolean;
function CurEl;
procedure Done;
end;
Для реалізації cтрічки зручно використовувати двухнаправлений лінійний список:
Порожня, якщо Current=EOList
та Current.^Prev=nil
ліворуч порожня, якщо Current.^Prev=nil
праворуч порожня, якщо
Current=EOList
MakePtr = ^Node
Node = record
Prev:NodePtr;
Elem:E;
Next:NodePtr;
End;
Дек (DEQ) – двовхідна черга (Double-Ended-Quene).
Різновиди деку:
дек з обмеженим входом - архів;
дек з обмеженим виходом - перелік або реєстр.
Реализация об'єкта типу дек:
DEQuene=object
procedure Init;
function Empty:boolean;
procedure EnQTail(El:E);
procedure DeQTail( var El:E);
procedure EnQHead(El:E);
procedure DeQHead( var El:E);
function Head:E;
function Tail:E;
procedure Done;
end;
Фізичне подання:
обмежений дек на базі масиву;
необмежений дек на базі лінійного двухнаправленого списку.
Для реалізації cтрічки зручно використовувати двухнаправлений лінійний список:
Порожня, якщо Current=EOList
та Current.^Prev=nil
ліворуч порожня, якщо Current.^Prev=nil
праворуч порожня, якщо
Current=EOList
MakePtr = ^Node
Node = record
Prev:NodePtr;
Elem:E;
Next:NodePtr;
End;