EasyТеория9 min

Стек

LIFO структура: реализация, классические задачи — скобки, Min Stack, калькулятор

Стек (Stack)

Что такое стек?

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

Push (добавление):        Pop (извлечение):

    | 4 | <- top           | 4 | -> удалён
    | 3 |                  | 3 | <- новый top
    | 2 |                  | 2 |
    | 1 |                  | 1 |
    +---+                  +---+

Операции стека

Операция Описание Сложность
push(x) Положить элемент на вершину O(1)
pop() Убрать элемент с вершины O(1)
peek/top() Посмотреть вершину O(1)
isEmpty() Проверка на пустоту O(1)
size() Количество элементов O(1)

Реализация

class Stack
{
    private array $items = [];

    public function push(int $val): void
    {
        $this->items[] = $val;
    }

    public function pop(): int
    {
        if ($this->isEmpty()) {
            throw new UnderflowException('Stack is empty');
        }
        return array_pop($this->items);
    }

    public function peek(): int
    {
        if ($this->isEmpty()) {
            throw new UnderflowException('Stack is empty');
        }
        return $this->items[count($this->items) - 1];
    }

    public function isEmpty(): bool
    {
        return count($this->items) === 0;
    }

    public function size(): int
    {
        return count($this->items);
    }
}

// In practice, just use array as stack:
$stack = [];
$stack[] = 1;          // push
$stack[] = 2;
array_pop($stack);     // pop -> 2
end($stack);           // peek -> 1
## Задача 1: Валидные скобки

Условие: строка содержит (){}[]. Проверить корректность.

function isValidParentheses(string $s): bool
{
    $stack = [];
    $matching = [')' => '(', '}' => '{', ']' => '['];

    for ($i = 0; $i < strlen($s); $i++) {
        $char = $s[$i];
        if (in_array($char, ['(', '{', '['])) {
            $stack[] = $char;
        } elseif (isset($matching[$char])) {
            if (empty($stack) || end($stack) !== $matching[$char]) {
                return false;
            }
            array_pop($stack);
        }
    }

    return empty($stack);
}
Визуализация для `{[()]}`:
Символ  Стек        Действие
  {     [{]         push
  [     [{, []      push
  (     [{, [, (]   push
  )     [{, []      pop (match)
  ]     [{]         pop (match)
  }     []          pop (match)
                    Стек пуст -> true

Задача 2: Min Stack

Условие: стек, который поддерживает push, pop, top и getMin — всё за O(1).

class MinStack
{
    private array $stack = [];
    private array $minStack = [];

    public function push(int $val): void
    {
        $this->stack[] = $val;
        if (empty($this->minStack) || $val <= end($this->minStack)) {
            $this->minStack[] = $val;
        }
    }

    public function pop(): void
    {
        $val = array_pop($this->stack);
        if ($val === end($this->minStack)) {
            array_pop($this->minStack);
        }
    }

    public function top(): int
    {
        return end($this->stack);
    }

    public function getMin(): int
    {
        return end($this->minStack);
    }
}
## Задача 3: Обратная польская нотация (RPN)
function evalRPN(array $tokens): int
{
    $stack = [];

    foreach ($tokens as $token) {
        if (in_array($token, ['+', '-', '*', '/'])) {
            $b = array_pop($stack);
            $a = array_pop($stack);
            $stack[] = match ($token) {
                '+' => $a + $b,
                '-' => $a - $b,
                '*' => $a * $b,
                '/' => intdiv($a, $b),
            };
        } else {
            $stack[] = (int) $token;
        }
    }

    return $stack[0];
}
## Задача 4: Упрощение пути (Simplify Path)
function simplifyPath(string $path): string
{
    $stack = [];
    $parts = explode('/', $path);

    foreach ($parts as $part) {
        if ($part === '..') {
            if (!empty($stack)) {
                array_pop($stack);
            }
        } elseif ($part !== '' && $part !== '.') {
            $stack[] = $part;
        }
    }

    return '/' . implode('/', $stack);
}
## Задача 5: Декодирование строки
function decodeString(string $s): string
{
    $stack = [];
    $currentString = '';
    $currentNum = 0;

    for ($i = 0; $i < strlen($s); $i++) {
        $char = $s[$i];

        if (ctype_digit($char)) {
            $currentNum = $currentNum * 10 + (int) $char;
        } elseif ($char === '[') {
            $stack[] = [$currentString, $currentNum];
            $currentString = '';
            $currentNum = 0;
        } elseif ($char === ']') {
            [$prevString, $num] = array_pop($stack);
            $currentString = $prevString . str_repeat($currentString, $num);
        } else {
            $currentString .= $char;
        }
    }

    return $currentString;
}
## Где используется стек
  • Вызов функций — стек вызовов (call stack)
  • Undo/Redo — в текстовых редакторах
  • Навигация — кнопки Назад/Вперёд в браузере
  • Парсинг выражений — компиляторы, калькуляторы
  • DFS — обход графов (итеративная реализация)
  • Backtracking — перебор с откатом

Запомни: Стек — это просто, но мощно. Если задача связана с вложенностью (скобки, HTML-теги, рекурсивные структуры) или с последовательной обработкой с возможностью отката — думай о стеке.

Итоги

  1. LIFO: последний пришёл — первый ушёл
  2. Все операции O(1)
  3. Валидные скобки — каноническая задача на стек
  4. Min Stack: параллельный стек минимумов
  5. RPN, декодирование строк, упрощение путей — всё через стек

Проверь себя

Что вернёт evalRPN для токенов `['3', '4', '+', '2', '*', '1', '+']`?

Что вернёт функция isValidParentheses для строки `"([)]"`?

Какова временная и пространственная сложность задачи проверки валидных скобок?

В MinStack при push значений 5, 3, 7, 1, 6 — что содержит minStack (стек минимумов)?

Какой результат выполнения следующего кода? ```php $stack = []; $stack[] = 1; $stack[] = 2; $stack[] = 3; array_pop($stack); $stack[] = 4; echo end($stack); ```

Code Challenges

Проверка скобок

PHP

Дана строка, содержащая только символы (, ), {, }, [ и ]. Определите, является ли входная строка валидной.

Test Cases

1. Input: ()→ Expected: true
2. Input: ()[]{}→ Expected: true
3. Input: (]→ Expected: false
4. Input: ([)]→ Expected: false
5. Input: {[]}→ Expected: true