Valid Parentheses

Valid Parentheses — это классическая задача на stack.

Смысл задачи: проверить, правильно ли закрываются скобки.

Например:

"()"
"()[]{}"
"{[]}"

валидно.

А вот:

"(]"
"([)]"
"((("
"]"

невалидно.

Когда мы видим открывающую скобку, мы кладём её в стек:

(
[
{

Когда мы видим закрывающую скобку, она должна закрывать последнюю
открытую
скобку.

То есть работает принцип:

последняя открытая скобка должна закрыться первой

А это ровно стек:

Last In — First Out

Последним вошёл — первым вышел.

Пример

Строка:

"{[]}"

Идём слева направо.

Видим {:

stack = ["{"]

Видим [:

stack = ["{", "["]

Видим ].

Она должна закрыть последнюю открытую скобку. Последняя — [. Всё хорошо.

stack = ["{"]

Видим }.

Она должна закрыть {. Всё хорошо.

stack = []

В конце стек пустой — значит все скобки закрылись правильно.

Ответ:

true

Невалидный пример

Строка:

"([)]"

Идём:

( -> stack = ["("]
[ -> stack = ["(", "["]

Теперь видим ).

Но последняя открытая скобка — [, а ) закрывает (.

То есть порядок сломан:

ожидали ], но получили )

Ответ:

false

Базовый алгоритм

  1. Создаём пустой стек.
  2. Если символ открывающий — кладём в стек.
  3. Если символ закрывающий:
  4. стек не должен быть пустым;
  5. верхняя скобка в стеке должна соответствовать этой закрывающей.
  6. В конце стек должен быть пустым.

Python

def is_valid(s):
    stack = []

    pairs = {
        ")": "(",
        "]": "[",
        "}": "{",
    }

    for ch in s:
        if ch in "([{":
            stack.append(ch)
        else:
            if not stack:
                return False

            top = stack.pop()

            if top != pairs[ch]:
                return False

    return len(stack) == 0

Java

import java.util.*;

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();

        Map<Character, Character> pairs = Map.of(
            ')', '(',
            ']', '[',
            '}', '{'
        );

        for (char ch : s.toCharArray()) {
            if (ch == '(' || ch == '[' || ch == '{') {
                stack.push(ch);
            } else {
                if (stack.isEmpty()) {
                    return false;
                }

                char top = stack.pop();

                if (top != pairs.get(ch)) {
                    return false;
                }
            }
        }

        return stack.isEmpty();
    }
}

Современнее в Java использовать Deque, а не старый Stack:

import java.util.*;

class Solution {
    public boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();

        Map<Character, Character> pairs = Map.of(
            ')', '(',
            ']', '[',
            '}', '{'
        );

        for (char ch : s.toCharArray()) {
            if (ch == '(' || ch == '[' || ch == '{') {
                stack.push(ch);
            } else {
                if (stack.isEmpty()) {
                    return false;
                }

                char top = stack.pop();

                if (top != pairs.get(ch)) {
                    return false;
                }
            }
        }

        return stack.isEmpty();
    }
}

Почему тут именно stack

Потому что вложенность скобок работает так:

{ [ ( ) ] }

Сначала открылась {, потом [, потом (.

Но закрываться они должны в обратном порядке:

( закрывается первой
[ закрывается второй
{ закрывается последней

То есть структура такая:

открытие:   {   [   (
закрытие:           )   ]   }

Это буквально стек.

Complexity

Время:

O(n)

Потому что мы один раз проходим по строке.

Память:

O(n)

В худшем случае вся строка может состоять из открывающих скобок:

"((((((((("

и тогда все они окажутся в стеке.

Главное различение

valid parentheses — это задача не про подсчёт количества скобок.

То есть нельзя просто проверить, что количество ( равно количеству ).

Например:

")("

Количество одинаковое, но строка невалидна.

Почему? Потому что закрывающая скобка появилась раньше открывающей.

Поэтому нужна не frequency map, а именно stack, потому что важен порядок
закрытия
.

Прокрутить вверх