Stack och Queue

Stack och Queue är specialiserade collections som följer specifika ordningsregler - perfekta för situationer där ordning verkligen spelar roll!

🎯 Efter denna artikel kommer du att:

  • Förstå LIFO vs FIFO - Last In First Out vs First In First Out 📚🚶‍♂️
  • **Använda Stack** - för undo-funktioner, funktionsanrop, och "senaste först" scenarion 📚
  • **Använda Queue** - för schemaläggning, bufferts, och "första först" scenarion 🚶‍♂️
  • Veta när att använda vad - och undvika vanliga misstag! 💡

📚 **Stack - LIFO Principen**

Grundläggande koncept

Stack = “trave” eller “hög” - tänk som en trave tallrikar eller böcker.

  • LIFO: Last In, First Out - sista in, första ut
  • Push: Lägg till på toppen
  • Pop: Ta bort från toppen
  • Peek: Kika på toppen utan att ta bort
using System;
using System.Collections.Generic;

Stack<string> books = new Stack<string>();

// Push - lägg till på toppen
books.Push("C# Grunderna");
books.Push("Advanced C#");
books.Push("Clean Code");

Console.WriteLine($"Antal böcker: {books.Count}"); // 3

// Peek - kika på toppen utan att ta bort
string topBook = books.Peek();
Console.WriteLine($"Överst: {topBook}"); // "Clean Code"
Console.WriteLine($"Antal efter Peek: {books.Count}"); // 3 (oförändrat)

// Pop - ta bort från toppen
string removedBook = books.Pop();
Console.WriteLine($"Tog bort: {removedBook}"); // "Clean Code"
Console.WriteLine($"Antal efter Pop: {books.Count}"); // 2

**Stack Methods och Properties**

Stack<int> numbers = new Stack<int>();

// Properties
Console.WriteLine($"Count: {numbers.Count}");     // 0

// Push - lägg till element (void method)
numbers.Push(10);
numbers.Push(20);
numbers.Push(30);

// Peek - kika på toppen (returnerar element)
int top = numbers.Peek();                         // 30
// Throws InvalidOperationException om stack är tom!

// Pop - ta bort och returnera från toppen
int removed = numbers.Pop();                      // 30
// Throws InvalidOperationException om stack är tom!

// Contains - kontrollera om element finns
bool hasValue = numbers.Contains(20);             // true

// ToArray - konvertera till array (i LIFO ordning!)
int[] array = numbers.ToArray();                  // {20, 10}

// Clear - ta bort alla element
numbers.Clear();

// TryPeek och TryPop (säkra versioner - .NET 6+)
if (numbers.TryPeek(out int topValue))
{
    Console.WriteLine($"Top: {topValue}");
}
else
{
    Console.WriteLine("Stack är tom");
}

Stack användningsområden

// 1. Undo-funktionalitet
Stack<string> undoStack = new Stack<string>();

void PerformAction(string action)
{
    undoStack.Push(action);
    Console.WriteLine($"Utförde: {action}");
}

void Undo()
{
    if (undoStack.Count > 0)
    {
        string lastAction = undoStack.Pop();
        Console.WriteLine($"Ångra: {lastAction}");
    }
}

PerformAction("Skapa fil");
PerformAction("Skriv text");
PerformAction("Formatera text");
Undo(); // Ångra: Formatera text
Undo(); // Ångra: Skriv text

// 2. Parenteser-matchning
bool IsValidParentheses(string input)
{
    Stack<char> stack = new Stack<char>();

    foreach (char c in input)
    {
        if (c == '(' || c == '[' || c == '{')
        {
            stack.Push(c);
        }
        else if (c == ')' || c == ']' || c == '}')
        {
            if (stack.Count == 0) return false;

            char opening = stack.Pop();
            if (!IsMatchingPair(opening, c)) return false;
        }
    }

    return stack.Count == 0;
}

bool IsMatchingPair(char opening, char closing)
{
    return (opening == '(' && closing == ')') ||
           (opening == '[' && closing == ']') ||
           (opening == '{' && closing == '}');
}

Console.WriteLine(IsValidParentheses("({[]})"));  // true
Console.WriteLine(IsValidParentheses("({[}])"));  // false

🚶‍♂️ **Queue - FIFO Principen**

Grundläggande koncept

Queue = “kö” - tänk som en kö i butiken eller på bussen.

  • FIFO: First In, First Out - första in, första ut
  • Enqueue: Ställ dig sist i kön
  • Dequeue: Första personen lämnar kön
  • Peek: Kika på första utan att ta bort
using System;
using System.Collections.Generic;

Queue<string> customerQueue = new Queue<string>();

// Enqueue - ställ sig sist i kön
customerQueue.Enqueue("Anna");
customerQueue.Enqueue("Bert");
customerQueue.Enqueue("Cilla");

Console.WriteLine($"Antal i kön: {customerQueue.Count}"); // 3

// Peek - kika på första utan att ta bort
string nextCustomer = customerQueue.Peek();
Console.WriteLine($"Nästa kund: {nextCustomer}"); // "Anna"
Console.WriteLine($"Antal efter Peek: {customerQueue.Count}"); // 3

// Dequeue - första kunden lämnar
string servedCustomer = customerQueue.Dequeue();
Console.WriteLine($"Betjänade: {servedCustomer}"); // "Anna"
Console.WriteLine($"Antal efter Dequeue: {customerQueue.Count}"); // 2

**Queue Methods och Properties**

Queue<int> taskQueue = new Queue<int>();

// Properties
Console.WriteLine($"Count: {taskQueue.Count}");   // 0

// Enqueue - lägg till element sist (void method)
taskQueue.Enqueue(1);
taskQueue.Enqueue(2);
taskQueue.Enqueue(3);

// Peek - kika på första (returnerar element)
int first = taskQueue.Peek();                     // 1
// Throws InvalidOperationException om queue är tom!

// Dequeue - ta bort och returnera första
int served = taskQueue.Dequeue();                 // 1
// Throws InvalidOperationException om queue är tom!

// Contains - kontrollera om element finns
bool hasValue = taskQueue.Contains(3);            // true

// ToArray - konvertera till array (i FIFO ordning!)
int[] array = taskQueue.ToArray();                // {2, 3}

// Clear - ta bort alla element
taskQueue.Clear();

// TryDequeue och TryPeek (säkra versioner - .NET Core 2.0+)
if (taskQueue.TryDequeue(out int nextTask))
{
    Console.WriteLine($"Nästa task: {nextTask}");
}
else
{
    Console.WriteLine("Queue är tom");
}

Queue användningsområden

// 1. Task scheduling - first come, first served
Queue<string> jobQueue = new Queue<string>();

void AddJob(string job)
{
    jobQueue.Enqueue(job);
    Console.WriteLine($"La till jobb: {job}");
}

void ProcessNextJob()
{
    if (jobQueue.Count > 0)
    {
        string job = jobQueue.Dequeue();
        Console.WriteLine($"Bearbetar: {job}");
    }
    else
    {
        Console.WriteLine("Inga jobb att bearbeta");
    }
}

AddJob("Backup databas");
AddJob("Skicka emails");
AddJob("Generera rapporter");

ProcessNextJob(); // Bearbetar: Backup databas
ProcessNextJob(); // Bearbetar: Skicka emails

// 2. Breadth-First Search (BFS) i träd/graf
void BreadthFirstSearch(TreeNode root)
{
    if (root == null) return;

    Queue<TreeNode> queue = new Queue<TreeNode>();
    queue.Enqueue(root);

    while (queue.Count > 0)
    {
        TreeNode current = queue.Dequeue();
        Console.WriteLine(current.Value);

        // Lägg till alla barn i kön
        foreach (TreeNode child in current.Children)
        {
            queue.Enqueue(child);
        }
    }
}

// 3. Buffer för streaming data
Queue<byte> dataBuffer = new Queue<byte>();
const int MAX_BUFFER_SIZE = 1024;

void AddDataToBuffer(byte[] data)
{
    foreach (byte b in data)
    {
        if (dataBuffer.Count >= MAX_BUFFER_SIZE)
        {
            // Ta bort äldsta data för att göra plats
            dataBuffer.Dequeue();
        }
        dataBuffer.Enqueue(b);
    }
}

🆚 Stack vs Queue - När använda vad?

**Använd Stack när:**

// ✅ LIFO behavior krävs
// ✅ Undo/Redo funktionalitet
Stack<ICommand> undoStack = new Stack<ICommand>();

// ✅ Parsning och evaluation (parenteser, uttryck)
Stack<char> expressionStack = new Stack<char>();

// ✅ Funktionsanrop (call stack simulation)
Stack<string> callStack = new Stack<string>();

// ✅ Backtracking algoritmer
Stack<Position> pathStack = new Stack<Position>();

// ✅ Browser history (back button)
Stack<string> browserHistory = new Stack<string>();

**Använd Queue när:**

// ✅ FIFO behavior krävs
// ✅ Task/job scheduling
Queue<ITask> taskQueue = new Queue<ITask>();

// ✅ Print job queues
Queue<PrintJob> printQueue = new Queue<PrintJob>();

// ✅ Breadth-first search
Queue<TreeNode> bfsQueue = new Queue<TreeNode>();

// ✅ Buffering (data streaming)
Queue<byte> streamBuffer = new Queue<byte>();

// ✅ Event handling (first registered, first processed)
Queue<EventHandler> eventQueue = new Queue<EventHandler>();

Performance Comparison

OperationStackQueueListArray
Add (Push/Enqueue)O(1)O(1)O(1)*N/A
Remove (Pop/Dequeue)O(1)O(1)O(1)*N/A
PeekO(1)O(1)O(1)O(1)
ContainsO(n)O(n)O(n)O(n)
MemoryMinimalMinimalDynamicFixed

*Amortized time - occasionally O(n) when internal array needs resizing

💡 Common Pitfalls och Best Practices

// ❌ Glöm inte att kontrollera om tom innan Pop/Dequeue
Stack<int> stack = new Stack<int>();
// int value = stack.Pop(); // InvalidOperationException!

// ✅ Kontrollera först
if (stack.Count > 0)
{
    int value = stack.Pop();
}

// ✅ Eller använd Try-metoder (.NET Core 2.0+)
if (stack.TryPop(out int safeValue))
{
    Console.WriteLine($"Got value: {safeValue}");
}

// ❌ Modifiera Stack/Queue medan du itererar
Stack<int> numbers = new Stack<int>(new[] {1, 2, 3});
foreach (int num in numbers)
{
    if (num > 1) numbers.Pop(); // InvalidOperationException!
}

// ✅ Kopiera först, eller använd while
Stack<int> numbersCopy = new Stack<int>(numbers);
while (numbersCopy.Count > 0)
{
    int num = numbersCopy.Pop();
    if (num > 1)
    {
        numbers.Pop(); // OK - modifierar olika collection
    }
}

🎯 Praktiska Tips

// Stack för "senaste först" scenarios
Stack<string> recentFiles = new Stack<string>();
recentFiles.Push("Document1.txt");
recentFiles.Push("Document2.txt");
string mostRecent = recentFiles.Peek(); // "Document2.txt"

// Queue för "första först" scenarios
Queue<Customer> serviceQueue = new Queue<Customer>();
serviceQueue.Enqueue(new Customer("Anna"));
serviceQueue.Enqueue(new Customer("Bert"));
Customer nextToServe = serviceQueue.Peek(); // Anna

// Kombinera för avancerade scenarios
Stack<ICommand> undoStack = new Stack<ICommand>();
Stack<ICommand> redoStack = new Stack<ICommand>();

void ExecuteCommand(ICommand command)
{
    command.Execute();
    undoStack.Push(command);
    redoStack.Clear(); // Clear redo när ny action utförs
}

void Undo()
{
    if (undoStack.Count > 0)
    {
        ICommand command = undoStack.Pop();
        command.Undo();
        redoStack.Push(command);
    }
}

void Redo()
{
    if (redoStack.Count > 0)
    {
        ICommand command = redoStack.Pop();
        command.Execute();
        undoStack.Push(command);
    }
}

🎓 TL;DR

  • **Stack**: LIFO (Last In, First Out) - använd för undo, parsing, backtracking
  • **Queue**: FIFO (First In, First Out) - använd för scheduling, buffering, BFS
  • Båda: O(1) för add/remove, O(n) för search
  • Säkerhet: Kontrollera Count > 0 innan Pop/Dequeue, eller använd Try-metoder

Obligatorisk dad-joke

Varför gillar programmerare Stack och Queue?

För att de alltid håller ordning på saker och ting - Stack tar det översta allvarligt, medan Queue är rättvis mot alla! 📚🚶‍♂️


Upp

Upp


Licens: Apache 2.0 | © 2023 Marcus Medina, Campus Mölndal. Alla rättigheter förbehållna.
Du får använda och modifiera detta verk enligt villkoren i Apache License, Version 2.0. Du får inte använda detta verk för kommersiella ändamål utan tillstånd från upphovsmannen.