Код
#статьи

Задача: создать очередь с помощью стеков

Решаем задачи, которые дают программистам на собеседованиях в IT-компаниях. Сегодня пытаемся понять, как сделать очередь из нескольких стеков.

Иллюстрация: Polina Vari для Skillbox Media

Сергей Голицын


Senior Java Developer в Covalent Inc. и преподаватель. Больше семи лет в Java-разработке. В свободное время судит хакатоны и делится опытом с начинающими программистами. Пишет статьи для «Хабра» и Medium. Ведёт телеграм-каналы «Полезные ссылки около Java» и Cracking code interview.


Условие. Реализовать first-in-first-out (FIFO) очередь, используя максимум два стека. Она должна поддерживать все стандартные функции обычной очереди — push, pop, peek и empty.

  • push — добавляет элемент в конец очереди;
  • pop — убирает элемент из начала и возвращает его как результат;
  • peek — возвращает элемент из начала очереди, но не убирает его;
  • empty — возвращает true, если очередь пустая, иначе — false.

Примечание: вы должны использовать только стандартные операции со стеками — push, peek/pop, size и empty. А если ваш язык не поддерживает стеки, то их можно реализовать через обычный список или двустороннюю очередь, но при этом использовать только функции, приведённые выше.

Ввод и вывод. Пример 1

Ввод:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]

Вывод:
[null, null, null, 1, 1, false]

Пояснение:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // очередь: [1]
myQueue.push(2); // очередь: [1, 2] (начало очереди — самый левый элемент)
myQueue.peek(); // вернёт 1
myQueue.pop(); // вернёт 1, очередь: [2]
myQueue.empty(); // вернёт false

Решить эту задачу самостоятельно и на разных языках программирования можно на LeetCode. Наше решение взято из телеграм-канала Сергея Cracking code interview.

Решение

Мы будем решать задачу с помощью двух стеков. Самое сложное — реализовать методы pop и peak. Но для начала давайте создадим наш класс:

static class MyQueue {

    Stack<Integer> current;
    Stack<Integer> tmp;

    public MyQueue() {
        current = new Stack<>();
        tmp = new Stack<>();
    }

Теперь давайте реализуем метод push:

public void push(int x) {
    current.push(x);
}

Всё просто — добавляем новый элемент в стек current.

Теперь давайте напишем метод pop. Для него нам нужно переместить все элементы из стека current в стек tmp. А перед этим — проверить стек tmp: если в нём есть элементы, то нам просто нужно вернуть элемент из этого стека.

В виде кода это выглядит так:

public int pop() {
    if (!tmp.isEmpty()) {
        return tmp.pop();
    }
    while (!current.isEmpty()) {
        tmp.push(current.pop());
    }
    return tmp.pop();

Чтобы продемонстрировать правильность работы этой фукнции, давайте посмотрим на пример:

  • push 1 → 1;
  • push 2 → 2, 1;
  • push 3 → 3, 2, 1;
  • pop → перемещаем всё в tmp: cur: 2, 1; tmp: 3 → cur: 1; tmp: 2, 3 → cur: ; tmp: 1, 2, 3;
  • теперь все элементы в tmp стоят в правильном порядке.

Получается, когда мы переносим элементы из одного стека в другой, этот процесс похож на функцию insert для очередей. Поэтому оно и работает.

Теперь по похожей логике давайте напишем функцию peek:

public int peek() {
    if (!tmp.isEmpty()) {
        return tmp.peek();
    }
    while (!current.isEmpty()) {
        tmp.push(current.pop());
    }
    return tmp.peek();

И последнее — метод empty:

public boolean empty() {
    if (!tmp.isEmpty()) {
        return tmp.isEmpty();
    }
    return current.isEmpty();
}

Здесь нам важно не забыть проверить вторую очередь.

Результаты

Временная сложность: лучший случай — O(1), худший — O(n).

Ёмкостная сложность: O(1) — нам нужно заранее определённое количество места в памяти.

Жизнь можно сделать лучше!
Освойте востребованную профессию, зарабатывайте больше и получайте от работы удовольствие.
Каталог возможностей
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована