Queues and stacks
Process items in arrival order with Queue<T>, or most-recent-first with Stack<T>.
10 min read
You will learn
- Explain FIFO and LIFO ordering
- Use Enqueue/Dequeue and Push/Pop/Peek
- Recognize problems that call for a stack or a queue
- Pick the right collection for a job
Order matters
Lists let you access any item at any time. Some problems are about which item comes next:
- Customers served in the order they arrived → queue (first in, first out — FIFO).
- Undoing the most recent action first → stack (last in, first out — LIFO).
Using the right collection makes this ordering automatic and the code self-explanatory.
Queue<T>: first in, first out
var line = new Queue<string>();
line.Enqueue("Ada"); // joins the back of the line
line.Enqueue("Bob");
line.Enqueue("Cy");
Console.WriteLine(line.Peek()); // Ada — look at the front without removing
Console.WriteLine(line.Dequeue()); // Ada — removes from the front
Console.WriteLine(line.Dequeue()); // Bob
Console.WriteLine(line.Count); // 1 Enqueue ─▶ [ Cy | Bob | Ada ] ─▶ Dequeue
back frontQueues are everywhere in backend systems: jobs waiting to be processed, messages between services, requests waiting for a worker. You'll meet message queues in Backend Fundamentals.
Stack<T>: last in, first out
var history = new Stack<string>();
history.Push("home"); // goes on top
history.Push("products");
history.Push("cart");
Console.WriteLine(history.Pop()); // cart — the most recent
Console.WriteLine(history.Peek()); // products — now on top Push ─▶ ┌──────────┐ ─▶ Pop
│ cart │ top
│ products │
│ home │
└──────────┘Natural uses: undo/redo, the browser's back button, matching brackets, evaluating expressions — and the call stack itself, which tracks which method called which (it's what you read in a stack trace).
Watch out
Dequeue() and Pop() throw InvalidOperationException on an empty collection. Check Count first, or use TryDequeue(out var item) / TryPop(out var item).
Example: undo
var text = new List<string>();
var undo = new Stack<string>();
void Type(string word) { text.Add(word); undo.Push(word); }
void Undo() { if (undo.TryPop(out _)) text.RemoveAt(text.Count - 1); }
Type("Hello"); Type("wrold"); Undo(); Type("world");
Console.WriteLine(string.Join(" ", text)); // Hello worldWhich collection should I use?
| You need to… | Use |
|---|---|
| Keep an ordered list, access by index, add/remove anywhere | List<T> |
| Look up values by a key | Dictionary<TKey, TValue> |
| Track unique items / fast "contains" | HashSet<T> |
| Process in arrival order | Queue<T> |
| Process most recent first | Stack<T> |
| Always take the smallest/most urgent item | PriorityQueue<TElement, TPriority> |
| A fixed number of items | T[] |
Try it
C# runtime not loaded
PriorityQueue always dequeues the item with the lowest priority value first.
Key takeaways
Queue<T>is FIFO:Enqueueat the back,Dequeuefrom the front.Stack<T>is LIFO:PushandPopat the top.Peeklooks without removing;TryDequeue/TryPopavoid exceptions on empty collections.- Choose collections by the access pattern you need.
Exercise
IsBalanced checks that every bracket — (), [] and {} — is closed in the right order (use a stack). LastToLeave plays "hot potato" with a queue: each round, move `passes` people from the front to the back, then the person at the front leaves. Return the last person remaining.
C# runtime not loaded
Hint 1
IsBalanced: push every opening bracket. For a closing bracket, the stack must not be empty and Pop() must return the matching opener. At the end, the stack must be empty.
Hint 2
LastToLeave: var queue = new Queue<string>(people); while (queue.Count > 1) { repeat passes times: queue.Enqueue(queue.Dequeue()); then queue.Dequeue(); }
Show solution
Console.WriteLine(IsBalanced("([]{})"));
Console.WriteLine(LastToLeave(new[] { "Ada", "Bob", "Cy", "Dee" }, 1));
bool IsBalanced(string text)
{
var open = new Stack<char>();
foreach (char c in text)
{
if (c is '(' or '[' or '{')
{
open.Push(c);
}
else if (c is ')' or ']' or '}')
{
if (open.Count == 0) return false;
char expected = c switch { ')' => '(', ']' => '[', _ => '{' };
if (open.Pop() != expected) return false;
}
}
return open.Count == 0;
}
string LastToLeave(string[] people, int passes)
{
var queue = new Queue<string>(people);
while (queue.Count > 1)
{
for (int i = 0; i < passes; i++)
{
queue.Enqueue(queue.Dequeue());
}
queue.Dequeue();
}
return queue.Dequeue();
}Quiz
4 questions · 3 correct to pass1.A Queue<int> receives Enqueue(1), Enqueue(2), Enqueue(3). What does Dequeue() return?
2.A Stack<int> receives Push(1), Push(2), Push(3). What does Pop() return?
3.What does Peek() do?
4.Which is a natural use for a stack?
To complete this lesson
- ○ Solve the exercise
- ○ Pass the quiz (3/4 correct)