{ }codelper

Learn › Craykon

Algorithms · 27

Stacks and Queues

A stack hands back the newest item first; a queue hands back the oldest first.

A stack and a queue hold values and give them back in a fixed order.
The example below is a school cafeteria.
tray x puts tray number x on top of the pile, and come name puts that person at the back of the line.
serve lets the person at the front take the top tray, or prints - if there is no tray or nobody waiting.
At the end it prints how many trays are left and how many people are still waiting.

q = Console.ReadLine().ToNumber();pile = []; // 스택: 끝이 맨 위waiting = []; // 큐: 뒤에 넣고, 맨 앞 번호(head)만 뒤로 옮겨요head = 0;for (i = 0; i < q; i++){    parts = Console.ReadLine().Trim().Split();    if (parts[0] == "tray")    {        pile.Add(parts[1].ToNumber()); // 맨 위에 올리기    }    else if (parts[0] == "come")    {        waiting.Add(parts[1]); // 맨 뒤에 서기    }    else if (pile.Count > 0 && head < waiting.Count)    {        name = waiting[head]; // 가장 먼저 온 사람        head += 1;        tray = pile[pile.Count - 1]; // 가장 나중에 올린 식판        pile.RemoveAt(pile.Count - 1);        Console.WriteLine(name + " " + tray);    }    else    {        Console.WriteLine("-");    }}Console.WriteLine(pile.Count + " " + (waiting.Count - head));
stdin
9
tray 3
tray 8
come ann
come bob
serve
tray 5
serve
serve
come cho
stdout
ann 8
bob 5
-
1 1

Key points

  • A stack gives back the last item put in first (last in, first out: LIFO).
    Like a pile of trays, things go in and come out at the same end.
  • A queue gives back the first item put in first (first in, first out: FIFO).
    Like a line of people, things go in at the back and come out at the front.
  • A stack needs only the end of a list.
    Adding at the end and removing from the end moves nothing else.
  • Removing the front of a list shifts everything left by one place.
    Measured on our judge, draining 200,000 items from the front took about 3.5 s with Python's list.pop(0), about 2 s with Java's ArrayList.remove(0), about 1.7 s with C#'s List.RemoveAt(0) and with C++'s vector.erase(begin()), and JavaScript's shift() took a little over 3 seconds and hit the 3-second limit.
    The same job with a real queue (deque, ArrayDeque, Queue, std::queue) finished within 0.05 s in every case, program start-up included.
  • JavaScript, TypeScript, C and Craykon have no built-in queue, so use an array (list) as the queue.
    Add at the end; to take, do not remove anything: read waiting[head] and then move the front index head forward by one.
    The queue is empty when head equals the number of items added, and the number waiting is items added − head.
    On our judge, taking 200,000 items this way in JavaScript took less than 0.01 s.
  • Check for empty before taking. Taking from an empty stack or queue stops with an error in some languages (Python, C#, Craykon) and quietly hands you undefined or null in others (JavaScript, Java's poll).
    In C and C++ nothing is promised about what happens.

How each language writes it

PythonA stack is a list: pile.append(x), pile.pop(), look at the top with pile[-1].
A queue is from collections import deque: waiting.append(x), waiting.popleft(), look at the front with waiting[0].
Empty check: if pile:.
Taking from an empty one raises IndexError.
queue.Queue is meant for passing values between threads, and its get() waits forever on an empty queue instead of raising an error, so use deque here
JavaScriptA stack is an array: pile.push(x), pile.pop(), look at the top with pile[pile.length - 1].
There is no queue type, so add with waiting.push(x) and take with waiting[head++].
pop() and shift() on an empty array give undefined with no error
TypeScriptSame as JavaScript.
The type of pile.pop() is number | undefined, so to store it in a number variable, check for empty and then write pile.pop()!
JavaBoth are ArrayDeque.
Stack: push, pop, peek.
Queue: add, poll, peek.
push adds at the front and add at the back, while pop, poll and peek all work on the front.
Empty check: isEmpty().
On an empty one pop() throws NoSuchElementException, while poll() and peek() return null
C#Stack<int>: Push, Pop, Peek.
Queue<string>: Enqueue, Dequeue, Peek.
The size is Count.
Taking from an empty one throws InvalidOperationException; TryPop(out x) and TryDequeue(out x) return false instead of stopping
C++#include <stack>: push, top, pop.
#include <queue>: push, front, pop.
pop() returns nothing, so read with top() or front() first.
Empty check: empty().
Calling top(), front() or pop() on an empty one is not guaranteed to stop with an error; nothing is promised about what happens (undefined behaviour)
CThere is no ready-made type, so use an array and indexes.
Stack: add with pile[top++] = x;, take with pile[--top].
Queue: add at waiting[tail++], take from waiting[head++].
Not empty means top > 0 and head < tail
CraykonBoth are lists.
Stack: pile.Add(x), the top is pile[pile.Count - 1], remove it with pile.RemoveAt(pile.Count - 1).
Queue: add with waiting.Add(x), read waiting[head], then head += 1.
Reading or removing from an empty list stops with "list is empty"

Common mistakes

Taking from an empty stack without any errorJavaScript

const pile = [];
const tray = pile.pop();
console.log(tray + 1);

→ NaN — pop() on an empty array does not stop; it gives undefined, and the wrong value quietly flows into the calculation

Check pile.length > 0 before taking.
TypeScript rejects the same code before it runs with 'tray' is possibly 'undefined'.

Adding with add and taking with popJava

Deque<Integer> pile = new ArrayDeque<>();
pile.add(1);
pile.add(2);
pile.add(3);
System.out.println(pile.pop());

→ 1 — not the 3 that went in last. add puts items at the very back and pop takes from the very front, so it behaved like a queue

Use matching pairs: push and pop for a stack, add and poll for a queue.
push adds at the front, so after pushing 1, 2, 3 with push, pop() gives 3.

Expecting pop() to return the valueC++

std::stack<int> pile;
pile.push(3);
int tray = pile.pop();

→ Compile error void value not ignored as it ought to be — pop() on std::stack and std::queue only removes and returns nothing

Read first with int tray = pile.top();, then remove with pile.pop();.
For a queue, read with front().

How languages differ
Work that handles "the most recent first" (undo, matching brackets) is a stack; work that handles things "in arrival order" (lines, waiting for a turn) is a queue.
A queue that only moves head keeps the taken values in the array, so in C size the array for the number of insertions, not for the most that wait at once.
Craykon lists hold at most 10,000 items, and draining 10,000 from the front with RemoveAt(0) still finished within 0.2 s on our judge.

Practice