Chapter 13: Queues, Deques and Circular Buffers
13.06Beginner / Intermediate5 topics15 min5 interactive animations

Queue transformations

Building one structure from another and reshaping a queue: a queue from two stacks, a stack from queues, rotation, reversal and the first non-repeating character in a stream.

Learning goals

  • Implement a queue with two stacks and explain its amortised cost
  • Rotate and reverse a queue using only queue and stack operations
  • Solve the first non-repeating character problem with a queue and counts

Prerequisites

The queue modelStacks and stack-based algorithms
13.06.1

Queue using two stacks

Reversing a stack's contents once turns LIFO into FIFO. Keep an in stack that receives every enqueue and an out stack that serves every dequeue. When out is empty, pop everything from in and push it onto out: the oldest value ends up on top. Values already in out are always older than any value in in, so the order is right.

A single dequeue can move many values, but each value moves across at most once, so over a run of operations the cost is O(1) amortised per operation. The animation shows the moves, and the final message counts them.

Java
1class QueueFromStacks {
2 private final Deque<Integer> in = new ArrayDeque<>(), out = new ArrayDeque<>();
3 void enqueue(int x) { in.push(x); }
4 int dequeue() {
5 if (out.isEmpty())
6 while (!in.isEmpty()) out.push(in.pop()); // reverses the order, once per batch
7 return out.pop();
8 }
9}

Best

O(1)

Average

O(1) amortised

Worst

O(n) for one dequeue

Space

O(n)

Queue built from two stacks

Interactive · edit the input

Your input

Try

in (enqueue pushes here)

empty

out (dequeue pops here)

empty
Setup

Two empty stacks. in receives new values; out serves dequeues. Reversing a stack's order once turns LIFO into FIFO.

Step 1 / 18
Java
1class QueueFromStacks {
2 private final Deque<Integer> in = new ArrayDeque<>(), out = new ArrayDeque<>();
3 void enqueue(int x) { in.push(x); }
4 int dequeue() {
5 if (out.isEmpty())
6 while (!in.isEmpty()) out.push(in.pop()); // reverses the order, once per batch
7 return out.pop();
8 }
9}

Live state

in

0

out

0

moves

0

No condition evaluated on this step
Execution trace (dry run table)Show 18 steps
StepLineStateWhat happens
1L2in=0 out=0 moves=0Two empty stacks. in receives new values; out serves dequeues. Reversing a stack's order once turns LIFO into FIFO.
2L3op=enqueue(1) in=1 out=0 moves=0enqueue(1): push onto in. O(1).
3L3op=enqueue(2) in=2 out=0 moves=0enqueue(2): push onto in. O(1).
4L3op=enqueue(3) in=3 out=0 moves=0enqueue(3): push onto in. O(1).
5L5op=dequeue() in=3 out=0 moves=0out is empty, so everything in in must move across.
6L6op=dequeue() moved=3 in=2 out=1 moves=1out.push(in.pop()) moves 3. The oldest value ends up on top of out.
7L6op=dequeue() moved=2 in=1 out=2 moves=2out.push(in.pop()) moves 2. The oldest value ends up on top of out.
8L6op=dequeue() moved=1 in=0 out=3 moves=3out.push(in.pop()) moves 1. The oldest value ends up on top of out.
9L7op=dequeue() returned=1 in=0 out=2 moves=3out.pop() returns 1: the value that was enqueued first.
10L3op=enqueue(4) in=1 out=2 moves=3enqueue(4): push onto in. O(1).
11L5op=dequeue() in=1 out=2 moves=3out is not empty, so its top is already the oldest value: no transfer needed.
12L7op=dequeue() returned=2 in=1 out=1 moves=3out.pop() returns 2: the value that was enqueued first.
13L5op=dequeue() in=1 out=1 moves=3out is not empty, so its top is already the oldest value: no transfer needed.
14L7op=dequeue() returned=3 in=1 out=0 moves=3out.pop() returns 3: the value that was enqueued first.
15L5op=dequeue() in=1 out=0 moves=3out is empty, so everything in in must move across.
16L6op=dequeue() moved=4 in=0 out=1 moves=4out.push(in.pop()) moves 4. The oldest value ends up on top of out.
17L7op=dequeue() returned=4 in=0 out=0 moves=4out.pop() returns 4: the value that was enqueued first.
18L9in=0 out=0 moves=4 return=[]Every value moved across at most once (4 moves in total), so dequeue is O(1) amortised. Queue order, front first: [].
13.06.2

Stack using two queues

A stack needs the newest value to leave first, but a queue serves the oldest. The usual fix is to make push expensive: after adding the new value, rotate the queue by moving every older value behind it, so the new value is at the front. Then pop is a plain dequeue.

With one queue the rotation is size - 1 times offer(poll()); with two queues the values are shuffled between them. Either way push costs O(n) and pop costs O(1). The animation “Push, pop, push” uses the single-queue form.

Java
1class StackFromQueue {
2 private final Queue<Integer> q = new ArrayDeque<>();
3 void push(int x) {
4 q.offer(x);
5 for (int i = 0; i < q.size() - 1; i++) q.offer(q.poll()); // move all older values behind x
6 }
7 int pop() { return q.remove(); } // the newest value is at the front
8 int top() { return q.element(); }
9}

Implementing a stack with one queue

Interactive · edit the input

Your input

Try

queue (front = stack top)

empty
Setup

A queue is first-in first-out, a stack is last-in first-out. Keeping the newest element at the queue's front makes pop and top trivial; push does the work.

Step 1 / 17
Java
1class StackUsingQueue {
2 private final Queue<Integer> q = new ArrayDeque<>(); // the front of the queue is the top of the stack
3 void push(int x) {
4 q.offer(x);
5 for (int i = 0; i < q.size() - 1; i++) q.offer(q.poll()); // rotate: the new element moves to the front
6 }
7 int pop() { return q.poll(); } // O(1)
8 int top() { return q.peek(); }
9}

Live state

No condition evaluated on this step
Execution trace (dry run table)Show 17 steps
StepLineStateWhat happens
1L2A queue is first-in first-out, a stack is last-in first-out. Keeping the newest element at the queue's front makes pop and top trivial; push does the work.
2L4op=push(1) size=1offer(1): it joins the back of the queue, which is the wrong end for a stack.
3L5top=11 is now at the front: it is the stack's top. push cost 0 rotations: O(n).
4L4op=push(2) size=2offer(2): it joins the back of the queue, which is the wrong end for a stack.
5L5rotation=1 moves=1Rotate: poll the front and offer it to the back (1 of 1).
6L5top=22 is now at the front: it is the stack's top. push cost 1 rotation: O(n).
7L4op=push(3) size=3offer(3): it joins the back of the queue, which is the wrong end for a stack.
8L5rotation=1 moves=2Rotate: poll the front and offer it to the back (1 of 2).
9L5rotation=2 moves=3Rotate: poll the front and offer it to the back (2 of 2).
10L5top=33 is now at the front: it is the stack's top. push cost 2 rotations: O(n).
11L7op=pop() returned=3poll() returns 3, the most recently pushed value still in the stack: O(1).
12L4op=push(4) size=3offer(4): it joins the back of the queue, which is the wrong end for a stack.
13L5rotation=1 moves=4Rotate: poll the front and offer it to the back (1 of 2).
14L5rotation=2 moves=5Rotate: poll the front and offer it to the back (2 of 2).
15L5top=44 is now at the front: it is the stack's top. push cost 2 rotations: O(n).
16L7op=pop() returned=4poll() returns 4, the most recently pushed value still in the stack: O(1).
17L9moves=5 return=[2, 1]Stack (top first): [2, 1]; 5 queue rotations in total. Either push or pop must cost O(n) when only queues are available.
13.06.3

Rotate queue

Rotating a queue by k moves the front value to the rear k times: q.offer(q.poll()). Rotating by the queue size changes nothing, so only k % size rotations are needed. A rotation by k leaves the values in the same cyclic order, shifted k places.

The cost is k % size poll/offer pairs, at most size - 1. Try the “k > size” preset to see that 6 rotations of a 4-element queue equal 2.

Java
1static void rotate(Queue<Integer> q, int k) {
2 int times = k % q.size(); // a full turn changes nothing
3 for (int i = 0; i < times; i++)
4 q.offer(q.poll()); // front goes to the rear
5}

Rotate a queue by k positions

Interactive · edit the input

Your input

Try

queue · front → rear

1front
2
3
4
5
6rear
Setup

k = 2, size = 6: only 2 rotations are needed (k % size).

Step 1 / 4
Java
1static void rotate(Queue<Integer> q, int k) {
2 int times = k % q.size(); // a full turn changes nothing
3 for (int i = 0; i < times; i++)
4 q.offer(q.poll()); // front goes to the rear
5}

Live state

k

2

size

6

times

2

k % size = 2true
Execution trace (dry run table)Show 4 steps
StepLineStateWhat happens
1L2k=2 size=6 times=2k = 2, size = 6: only 2 rotations are needed (k % size).
2L4i=0 moved=1 times=2poll() takes 1 from the front and offer() puts it at the rear.
3L4i=1 moved=2 times=2poll() takes 2 from the front and offer() puts it at the rear.
4L5return=[3, 4, 5, 6, 1, 2]Rotated queue: [3, 4, 5, 6, 1, 2]. It cost 2 poll/offer pairs.
13.06.4

Reverse queue

A queue cannot be reversed in place using only enqueue and dequeue, but a stack can do it: move every value from the queue onto a stack (the front ends up deepest), then move them back (the old rear is now popped first and becomes the new front). It is O(n) time and O(n) extra space.

A recursive version does the same with the call stack: dequeue the front, reverse the rest, enqueue the saved value at the end.

Java
1static void reverse(Queue<Integer> q) {
2 Deque<Integer> stack = new ArrayDeque<>();
3 while (!q.isEmpty()) stack.push(q.poll()); // front first in, so the front ends deepest
4 while (!stack.isEmpty()) q.offer(stack.pop()); // top first out: the old rear is now the front
5}

Reverse a queue with a stack

Interactive · edit the input

Your input

Try

queue · front → rear

1front
2
3
4
5rear

stack

empty
Setup

A queue gives values out oldest first, a stack newest first. Passing everything through a stack flips the order.

Step 1 / 12
Java
1static void reverse(Queue<Integer> q) {
2 Deque<Integer> stack = new ArrayDeque<>();
3 while (!q.isEmpty()) stack.push(q.poll()); // front first in, so front ends deepest
4 while (!stack.isEmpty()) q.offer(stack.pop()); // top first out: the old rear is now the front
5}

Live state

queue

5

stack

0

No condition evaluated on this step
Execution trace (dry run table)Show 12 steps
StepLineStateWhat happens
1L2queue=5 stack=0A queue gives values out oldest first, a stack newest first. Passing everything through a stack flips the order.
2L3moved=1 queue=4 stack=1poll() 1 from the queue and push it on the stack.
3L3moved=2 queue=3 stack=2poll() 2 from the queue and push it on the stack.
4L3moved=3 queue=2 stack=3poll() 3 from the queue and push it on the stack.
5L3moved=4 queue=1 stack=4poll() 4 from the queue and push it on the stack.
6L3moved=5 queue=0 stack=5poll() 5 from the queue and push it on the stack.
7L4moved=5 queue=1 stack=4pop() 5 from the stack and offer() it to the queue.
8L4moved=4 queue=2 stack=3pop() 4 from the stack and offer() it to the queue.
9L4moved=3 queue=3 stack=2pop() 3 from the stack and offer() it to the queue.
10L4moved=2 queue=4 stack=1pop() 2 from the stack and offer() it to the queue.
11L4moved=1 queue=5 stack=0pop() 1 from the stack and offer() it to the queue.
12L5return=[5, 4, 3, 2, 1]Reversed queue: [5, 4, 3, 2, 1]. O(n) time, O(n) extra space for the stack.
13.06.5

First non-repeating stream

Given a stream of characters, report after each character the first one that has appeared exactly once so far. Keep a count per character and a queue of candidates in arrival order. A new character joins the queue; before answering, remove characters from the *front* while their count is above 1. The front is then the answer, or # if the queue is empty.

The removal is lazy: a repeated character stays in the queue until it reaches the front. Each character enters once and leaves at most once, so the whole stream is O(n). Try “Late survivor” (abbacd): the answer changes from a to # and later to c.

Java
1static String firstNonRepeating(String s) {
2 int[] count = new int[26];
3 Deque<Character> q = new ArrayDeque<>();
4 StringBuilder out = new StringBuilder();
5 for (char c : s.toCharArray()) {
6 if (++count[c - 'a'] == 1) q.addLast(c); // first sighting: a candidate
7 while (!q.isEmpty() && count[q.peekFirst() - 'a'] > 1) q.pollFirst(); // drop repeats at the front
8 out.append(q.isEmpty() ? '#' : q.peekFirst());
9 }
10 return out.toString();
11}

Best

O(n)

Average

O(n)

Worst

O(n)

Space

O(1) for counts, O(k) for the queue

First non-repeating character in a stream

Interactive · edit the input

Your input

Try

stream so far

empty

queue of candidates

empty

counts

none

answer so far

none yet
Setup

After every character we must report the first character that has appeared exactly once so far. The queue keeps candidates in arrival order.

Step 1 / 19
Java
1static String firstNonRepeating(String s) {
2 int[] count = new int[26];
3 Deque<Character> q = new ArrayDeque<>();
4 StringBuilder out = new StringBuilder();
5 for (char c : s.toCharArray()) {
6 if (++count[c - 'a'] == 1) q.addLast(c); // first sighting: a candidate
7 while (!q.isEmpty() && count[q.peekFirst() - 'a'] > 1) q.pollFirst(); // drop repeats at the front
8 out.append(q.isEmpty() ? '#' : q.peekFirst());
9 }
10 return out.toString();
11}

Live state

length

7

No condition evaluated on this step
Execution trace (dry run table)Show 19 steps
StepLineStateWhat happens
1L2length=7After every character we must report the first character that has appeared exactly once so far. The queue keeps candidates in arrival order.
2L6c=a count=1'a' is new: count 1, and it joins the back of the candidate queue.
3L8answer=aThe first non-repeating character is 'a'.
4L6c=a count=2'a' has now appeared 2 times: it is no longer a candidate, but it may still sit in the queue.
5L7dropped=aThe front 'a' repeats, so it is removed. Dropping is lazy: only the front is ever checked.
6L8answer=#No character has appeared exactly once: report '#'.
7L6c=b count=1'b' is new: count 1, and it joins the back of the candidate queue.
8L8answer=bThe first non-repeating character is 'b'.
9L6c=c count=1'c' is new: count 1, and it joins the back of the candidate queue.
10L8answer=bThe first non-repeating character is 'b'.
11L6c=b count=2'b' has now appeared 2 times: it is no longer a candidate, but it may still sit in the queue.
12L7dropped=bThe front 'b' repeats, so it is removed. Dropping is lazy: only the front is ever checked.
13L8answer=cThe first non-repeating character is 'c'.
14L6c=d count=1'd' is new: count 1, and it joins the back of the candidate queue.
15L8answer=cThe first non-repeating character is 'c'.
16L6c=c count=2'c' has now appeared 2 times: it is no longer a candidate, but it may still sit in the queue.
17L7dropped=cThe front 'c' repeats, so it is removed. Dropping is lazy: only the front is ever checked.
18L8answer=dThe first non-repeating character is 'd'.
19L10return="a#bbccd"Answer string: "a#bbccd". Each character is added and removed at most once, so the whole stream costs O(n).

Practice ladder

Now try it yourself

Easy

  • Implement Queue using Stacks
  • Implement Stack using Queues

Medium

  • Design Circular Queue
  • First Unique Character in a String

Advanced

  • Sliding Window Maximum
  • Design Hit Counter

Key takeaways

  • Two stacks make a queue: enqueue on in, dequeue from out, refill out only when empty (O(1) amortised).
  • A stack from queues costs O(n) for either push or pop.
  • A queue of candidates plus counts answers first-non-repeating-in-a-stream in O(n).