Структуры данных: стеки и очереди

Записи и оператор with

Записи используются для создания своих типов данных

{ Обьявление записи - тип "Точка" }  
type 
  TPoint = Record 
    x,y : double;
  end;
  
{ Функция, вычисляющая расстояние между точками }   
function dist( A,B : TPoint ):double;
begin
  dist := sqrt( (A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y) );
end;

{ Использование записей }
var A,B : TPoint;
begin
  { Инициализируем координаты точек }
  A.x := 1; A.y := 2;
  B.x := 10; B.y := 11;
  writeln( dist(A,B) );   
end.

Использование with:

{ Обьявление записи - тип "Персонаж в игре" }
type 
  TUnit = Record 
    x,y : integer; { Координаты клетки где стоит персонаж }  
    name : string; { Имя персонажа }
  end;

var Unit1 : TUnit;

{ Инициализация без with }
Unit1.x := 2; 
Unit1.y := 3;
Unit1.name := 'SUPER-HERO';

{ Инициализация с with }
with Unit1 do begin
  x := 2; 
  y := 3;
  name := 'SUPER-HERO';
end;  

Реализация Стека и Очереди на базе массива

Стек (англ. stack — стопка) — структура данных с методом доступа к элементам LIFO (англ. Last In — First Out, «последним пришёл — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно снять верхнюю.

const StackSize = 10000; { Размер стека (сколько в него можно положить элементов) }

{ === Хранение стека === }
var
  Stack : array [1..StackSize] of Integer; { Массив для хранения стека }
  StackTop : Integer = 0; { Вершина стека - индекс в массиве Stack }

{ === Операции со стеком === }

{ Стек пуст? }
function isEmpty : Boolean;
begin
  isEmpty := StackTop = 0;
end;

{ Положить значение на вершину стека }
procedure Push( Value : Integer );
begin
  assert( StackTop < StackSize, 'Стек полон! Больше положить в него нельзя!');
  Inc(StackTop);
  Stack[StackTop] := Value;
end;

{ Забрать значение с вершины стека }
function Pop : Integer;
begin
  assert( not isEmpty, 'Нельзя извлечь элемент, потому что стек пуст!');
  Pop := Stack[StackTop];
  Dec( StackTop );
end;

{ === Тестирование работы стека === }
begin
  Writeln(isEmpty); { Выводит "TRUE" - стек пуст }
  Push(2); { В стеке: 2 }
  Writeln(isEmpty); { Выводит "FALSE" - стек не пуст }
  Push(5); { В стеке: 2, 5 }
  Writeln(Pop); { Выводит "5", в стеке: 2 }
  Writeln(Pop); { Выводит "2", в стеке пусто }
end.

Очередь — структура данных с дисциплиной доступа к элементам «первый пришёл — первый вышел» (FIFO, First In — First Out). Добавление элемента (принято обозначать словом enqueue — поставить в очередь) возможно лишь в конец очереди, выборка — только из начала очереди (что принято называть словом dequeue — убрать из очереди), при этом выбранный элемент из очереди удаляется.

Очередь

 

const QSize = 10000; { Размер очереди (сколько в неё можно положить элементов) }
var
  Q : array [1..QSize] of Integer; { Массив для хранения очереди }
  Q_Start : Integer = 1; { Указывает на голову очереди }
  Q_End : Integer = 1; { Указывает на элемент, который заполнится, когда в очередь войдёт новый элемент }

{ = Операции с очередью = }

{ Очередь пуста? }
function isEmpty : Boolean;
begin
  isEmpty := Q_Start = Q_End;
end;

{ Положить значение в конец очереди }
procedure Put( Value : Integer );
begin
  Q[Q_End] := Value;
  Dec(Q_End);
  { Поддержка закольцованности очереди }
  if Q_End < 1 then Q_End := QSize;
end;

{ Забрать значение с начала очереди }
function Get : Integer;
begin
  assert( not isEmpty, 'В очереди ничего нет!');
  Get := Q[Q_Start];
  Dec(Q_Start);
  { Поддержка закольцованности очереди }
  if Q_Start < 1 then Q_Start := QSize;
end;

begin
  Writeln(isEmpty); { Выводит "TRUE" - очередь пуста }
  Put(2); { В очереди: 2 }
  Writeln(isEmpty); { Выводит "FALSE" - очередь не пуста }
  Put(5); { В очереди: 5, 2 }
  Writeln(Get); { Выводит "2", в очереди: 5 }
  Writeln(Get); { Выводит "5", в очереди пусто }
end.

Реализация на C/C++

Реализация Стека

#include <iostream>
#include <assert.h>

using namespace std;

// Определение класса Стек
// Шаблон с двумя параметрами: 
//   T - тип элемента (например: int, char, char*)
//   size - размер стека (целое число)
template <typename T,int size>
class Stack{  
  T data[size]; // Данные стека   
  int count; // Количество элементов в стеке 
  // count указывает на ячейку после последнего элемента в стеке
  // Например, если count = 1, то последний элемент это data[0]
public:
  // Конструктор
  Stack(){ count = 0; };
  // Стек пуст?
  bool isEmpty(){ 
    return count <= 0; 
  };
  // Стек полон?
  bool isFull(){ 
    return count >= size; 
  };
  // Добавить на вершину стека
  void push(T value){ 
    assert(!isFull()); // Можно добавить только если стек ещё не полон
    data[count++] = value; // Записываем значение в массив и сдвигаем счётчик вправо
  }
  // Снять с вершины стека
  T pop(){ 
    assert(!isEmpty()); // Можно снять только если стек не пуст
    return data[--count]; // Уменьшаем счётчик (сдвигаем влево) и возвращаем значение
  }
};

// Основная программа - тестирование стека
int main() {  
  Stack<int,5> s; // Создаём пример стека  
  assert(s.isEmpty()); // Сейчас стек должен быть пустым    
  s.push(5); // Добавляем один элемент
  assert(!s.isEmpty());
  assert(s.pop() == 5); 
  assert(s.isEmpty());
  assert(!s.isFull());
  s.push(1); s.push(2); s.push(3); 
  s.push(4); assert(!s.isFull()); // Стек ещё не полон после добавления четвёртого элемента
  s.push(5); assert(s.isFull());

  Stack<char,2> cs; // Заводим другой пример стека - с символами в качестве элементов
  cs.push('a');

  return 0;
}

Реализация очереди (циклической очереди)

#include <iostream>
#include <assert.h>

using namespace std;

// Определение класса Очередь
// Шаблон с двумя параметрами: 
//   T - тип элемента (например: int, char, char*)
//   size - размер очереди (целое число)
template <typename T,int size>
class Queue{  
  T data[size]; // Данные очереди
  int head; // Голова очереди (первый элемент)
  int tail; // Хвост очереди (последний элемент)
public:
  // Конструктор
  Queue(){ head = -1; tail = 0; };
  // Очередь пуста?
  bool isEmpty(){ 
    return head < tail; 
  };
  // Очередь полна?
  bool isFull(){ 
    return len() >= size; 
  };
  // Текущая длина очереди
  int len(){
    return head-tail+1;
  };
  // Добавить в начало очереди
  void put(T value){ 
    assert(!isFull()); // Можно добавить только если очередь не полна
    ++head; // Сдвигаем голову вправо 
    data[head % size] = value; // Записываем в ячейку с номером по модулю
    // -----------------------------------------
    // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
    // -----------------------------------------
    // Количество ячеек - 10, максимальный размер очереди - 10
    // Куда должен пойти 10-ый элемент? На 0-ую позицию
    // Куда должен пойти 20-ый элемент? Тоже на 0-ую позицию
    // Цикличность очереди обепечивается тем, что мы индекс элемента всегда берём по модулю максимального размера очереди 
  }
  // Взять из конца очереди
  T get(){ 
    assert(!isEmpty()); // Можно снять только если очередь не полна
    return data[tail++ % size]; // Забираем значение из хвоста и двигаем хвост вправо
  }
};

// Основная программа - тестирование стека
int main() {  
  Queue<int,5> s; // Создаём пример стека  
  assert(s.isEmpty()); // Сейчас очередь должна быть пуста
  s.put(5); // Добавляем один элемент
  assert(!s.isEmpty());
  assert(s.get() == 5); 
  assert(s.isEmpty());
  assert(!s.isFull());
  s.put(1); s.put(2); s.put(3); 
  s.put(4); assert(!s.isFull()); 
  s.put(5); assert(s.isFull());

  return 0;
}