Стек – абстрактный тип данных (АТД) с дисциплиной доступа LIFO (last in – first out): последним пришёл – первым обслужен. Он лежит в основе вызовов функций и рекурсии, разбора скобочных структур, вычисления выражений, итеративных обходов графов, механизмов «отмена/повтор» и др. В материале строго определены операции стека и их инварианты, разобраны классические реализации (на массиве и на связном списке), дана амортизированная оценка для динамического массива, описан стек вызовов и активационные записи, приведены эталоны псевдокода и частые ошибки. Связь с ЕГЭ проявляется в задачах на трассировку, анализ сложности, работу со строками и массивами, моделирование автоматов. Завершает текст практикум из пяти упражнений формата «условие → критерии → подсказки».
Определение (АТД «стек»). Стек – структура, поддерживающая конечное множество значений домена T и операции:
Дисциплина LIFO. Пусть последовательность операций заканчивается вставкой push(a1), push(a2), …, push(ak). Тогда первые k успешных вызовов pop() вернут элементы в порядке ak, ak-1, …, a1.
Инварианты корректности.
Сложность по определению АТД. При корректной реализации push/pop/top/isEmpty/size работают за O(1) по времени; память – пропорциональна количеству хранимых элементов.
Для любой корректной реализации:
На массиве (фиксированная ёмкость)
Храним массив A[0..capacity−1] и индекс t – число элементов (также позиция для следующей вставки).
push(x): A[t] := x; t := t + 1.
pop(): t := t − 1; return A[t].
Инвариант: 0 ≤ t ≤ capacity.
Плюсы: простота, высокая пространственная локальность. Минусы: риск переполнения (overflow).
На массиве с динамическим ростом
При попытке push при t = capacity создаём новый массив ёмкости α·capacity (обычно α=2), копируем элементы, переназначаем указатель.
Амортизированная оценка. Копирование происходит редко; стоимость «дорогих» расширений распределяется по множеству дешёвых вставок ⇒ средняя стоимость push остаётся O(1) амортизированно.
На односвязном списке
Храним указатель head на верхний узел Node{value, next}.
push(x): head := new Node(x, head).
pop(): v := head.value; head := head.next; return v.
Плюсы: нет переполнения до исчерпания памяти. Минусы: больше накладных расходов на узлы, хуже локальность.

Активационная запись (stack frame) – блок данных, создаваемый при вызове процедуры/функции; содержит возвращаемый адрес, сохранённые регистры, параметры, локальные переменные, временные значения. Вызов функции «толкает» новую запись в стек; возврат «снимает» её.
Рекурсия. Каждая рекурсивная глубина создаёт новый кадр.
Ошибки уровня стека.
Проверка балансировки скобок. Проходим строку слева направо; открывающие кладём в стек; при закрывающей требуем совпадение типов с вершиной. Стек пуст в конце – строка корректна.
Вычисление выражений в ОПЗ (RPN). Числа – push; оператор – снимаем нужное число аргументов, вычисляем, push результата.
Преобразование инфикс → постфикс (шунтовочный алгоритм). Стек операторов хранит приоритеты и ассоциативность; выход – очередь/список.
Итеративный DFS. В стек кладём вершину; в цикле снимаем, посещаем, кладём непосещённых соседей.
Undo/Redo (двойной стек). Основной стек – история действий, вспомогательный – «будущее»; новые действия очищают «будущее».
Динамический стек на массиве
init():
A := new array[initial_capacity]
t := 0
push(x):
if t = capacity(A):
B := new array[2*capacity(A)]
copy A -> B
A := B
A[t] := x
t := t + 1
pop():
require t > 0
t := t - 1
return A[t]
top():
require t > 0
return A[t-1]
size(): return t
isEmpty(): return t = 0
Балансировка скобок
isBalanced(s):
S := empty stack of char
for each c in s:
if c in {'(', '[', '{'}: push(S, c)
else if c in {')', ']', '}':
if isEmpty(S): return false
o := pop(S)
if not matches(o, c): return false
return isEmpty(S)
matches(o,c): return (o='(' and c=')') or (o='[' and c=']') or (o='{' and c='}')
Оценка RPN
evalRPN(tokens):
S := empty stack of int
for t in tokens:
if t is number: push(S, t)
else: // бинарный оператор
b := pop(S); a := pop(S)
push(S, apply(t, a, b))
return pop(S)
Итеративный DFS
DFS_iter(G, s):
S := empty stack of vertex
visited := set()
push(S, s)
while not isEmpty(S):
v := pop(S)
if v in visited: continue
visit(v)
add v to visited
for u in neighbors(v) in reverse order:
if u not in visited: push(S, u)
Undo/Redo двумя стеками
do(action):
apply(action)
push(UNDO, action)
clear(REDO)
undo():
require not isEmpty(UNDO)
a := pop(UNDO)
apply(inverse(a))
push(REDO, a)
redo():
require not isEmpty(REDO)
a := pop(REDO)
apply(a)
push(UNDO, a)
Упражнение 1. Трассировка массива-стека
Условие. Имеется пустой динамический стек. Последовательно выполнить операции:
push(3), push(1), push(4), pop(), push(1), push(5), pop(), pop(), push(9)
Укажите значение на вершине и размер стека после каждой операции.
Запишите содержимое стека (снизу вверх) в конце.
Критерии. Правильное обновление вершины и счётчика; корректная последовательность значений.
Подсказки. После pop() вершина – предыдущий элемент; размер уменьшается на 1.
Упражнение 2. Балансировка скобок с несколькими типами
Условие. Для строки
s = "{[(a+b)*c] + (d/[e-f])}"
проведите работу алгоритма §6.2: выпишите содержимое стека после обработки каждого символа-скобки.
Критерии. Точное соответствие открывающих/закрывающих; стек пуст в конце.
Подсказки. Игнорируйте не-скобочные символы; при закрывающей обязательна проверка совпадения типов.
Упражнение 3. Инфикс → постфикс (шунтовочный алгоритм)
Условие. Преобразуйте выражение
A + B*(C - D) + E
в постфиксную форму и вычислите значение при A=2, B=3, C=10, D=4, E=5.
Критерии. Правильный приоритет * над +, корректная обработка скобок, корректное использование стека операторов.
Подсказки. Ожидаемая ОПЗ: A B C D - * + E + ; затем применить стек для вычисления.
Упражнение 4. Рекурсия vs явный стек
Условие. Реализована рекурсивная функция вычисления факториала F(n) со стандартным базовым случаем F(0)=1.
Сколько активационных записей будет одновременно на стеке для n=5?
Сымитируйте вычисление с явным стеком значений: покажите последовательность push/pop и максимальную высоту стека.
Критерии. Осознание «глубина = n+1» для входов 0..n; корректная последовательность операций при замене рекурсии на явный стек.
Подсказки. В явной симуляции удобно хранить пары «текущий k, промежуточный результат».
Упражнение 5. Undo/Redo двумя стеками
Условие. Последовательность действий в текстовом редакторе:
type(A), type(B), type(C), undo, type(D), undo, redo, type(E)
Смоделируйте содержимое стеков UNDO и REDO после каждой операции и укажите итоговое содержимое текста.
Критерии. Новое действие после undo очищает стек REDO; redo возможен только при непустом REDO.
Подсказки. Текст – конкатенация применённых действий; undo добавляет инверсии в REDO.
Стек – минималистичная, но чрезвычайно мощная структура, обеспечивающая предсказуемую дисциплину доступа, строгие инварианты и O(1)-операции. Его присутствие на всех уровнях – от аппаратного протокола вызовов до алгоритмов разбора и обходов – делает владение стеком обязательным навыком. Для ЕГЭ владение стековыми приёмами означает уверенность в задачах на строки, выражения, графы и трассировку. Следуя изложенным правилам реализации и проверяя решения через инварианты, можно систематически избегать ошибок и обосновывать сложность. Пять упражнений закрепляют навыки в формах, максимально близких к экзаменационным.