Skip to content
IRC-Coding IRC-Coding
Data Structures Arrays Stacks Queues Heaps Trees Graphs Performance Analysis Big O Notation

Data Structures Guide: Arrays, Stacks, Queues, Heaps & Graphs

Essential data structures overview with performance analysis. Arrays O(1) access, Stacks/Queues LIFO/FIFO, Heaps for priority queues, Trees O(log n).

S

schutzgeist

2 min read

Data Structures Overview: Array, Stack, Queue, Heap, Tree & Graph

This article is a compact overview of the most important data structures – with performance analysis, use cases, and Big-O notation.

In a Nutshell

The core data structures Array, Stack, Queue, Heap, Tree, and Graph form the foundation of efficient algorithms. They differ in access time, memory requirements, and applications.

Compact Technical Description

Data structures organize data for efficient access and processing. Each structure has specific properties and optimal areas of application.

Performance Overview:

Array

  • Access: O(1) - Direct via index
  • Insert/Delete: O(n) - Elements must be shifted
  • Memory: O(n) - Contiguous memory
  • Use: Fixed size, frequent random access

Stack (LIFO)

  • Push/Pop: O(1) - Only topmost element
  • Memory: O(n) - Dynamic or fixed
  • Use: Method calls, backtracking, parser

Queue (FIFO)

  • Enqueue/Dequeue: O(1) - Front/Back
  • Memory: O(n) - Circular queue possible
  • Use: Wait queues, BFS, buffer

Heap (Priority Queue)

  • Insert: O(log n) - Maintain heap property
  • Extract Min/Max: O(log n) - Remove root
  • Peek: O(1) - View minimum/maximum
  • Use: Priorities, scheduling, heap sort

Tree (BST)

  • Search: O(log n) - Balanced
  • Insert/Delete: O(log n) - Balanced
  • Memory: O(n) - Node-based
  • Use: Ordered data, databases

Graph

  • Traversal: O(V+E) - V=nodes, E=edges
  • Memory: O(V+E) - Adjacency list
  • Use: Networks, route planning

Exam-Relevant Key Points

  • Array: O(1) access, fixed size, contiguous memory
  • Stack: LIFO principle, push/pop O(1), call stack
  • Queue: FIFO principle, enqueue/dequeue O(1), buffer
  • Heap: Priority queue, O(log n) insert/extract, O(1) peek
  • Tree: Hierarchical, O(log n) with balancing, BST
  • Graph: Node-edge structure, BFS/DFS O(V+E)
  • Big-O Notation: Time and space complexity
  • IHK-relevant: Foundation for algorithms and efficiency

Core Components

  1. Array: Indexed collection with direct access
  2. Stack: LIFO data structure for backtracking
  3. Queue: FIFO data structure for wait queues
  4. Heap: Priority-based tree structure
  5. Tree: Hierarchical parent-child relationships
  6. Graph: Network of nodes and edges
  7. Performance: Big-O analysis of operations
  8. Application: Optimal structure selection

Practical Examples

1. Array vs ArrayList Performance

import java.util.*;

public class ArrayPerformance {
    public static void main(String[] args) {
        final int GROESSE = 100_000;
        
        // Array (feste Größe)
        long start = System.nanoTime();
        int[] array = new int[GROESSE];
        for (int i = 0; i < GROESSE; i++) {
            array[i] = i; // O(1) Schreiben
        }
        long arrayZeit = System.nanoTime() - start;
        
        // ArrayList (dynamisch)
        start = System.nanoTime();
        List<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < GROESSE; i++) {
            arrayList.add(i); // O(1) amortisiert
        }
        long arrayListZeit = System.nanoTime() - start;
        
        // Random Access Test
        Random random = new Random();
        
        start = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            int index = random.nextInt(GROESSE);
            int wert = array[index]; // O(1)
        }
        long arrayAccess = System.nanoTime() - start;
        
        start = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            int index = random.nextInt(GROESSE);
            int wert = arrayList.get(index); // O(1)
        }
        long arrayListAccess = System.nanoTime() - start;
        
        System.out.println("Array fill time: " + arrayZeit / 1_000_000 + " ms");
        System.out.println("ArrayList fill time: " + arrayListZeit / 1_000_000 + " ms");
        System.out.println("Array access: " + arrayAccess / 1000 + " μs");
        System.out.println("ArrayList access: " + arrayListAccess / 1000 + " μs");
    }
}

2. Stack vs Queue Application

import java.util.*;

public class StackQueueDemo {
    public static void main(String[] args) {
        // Stack for bracket checking (LIFO)
        String ausdruck = "{[()()]}";
        if (pruefeKlammer(ausdruck)) {
            System.out.println("Expression '" + ausdruck + "' is valid");
        }
        
        // Queue for printer queue (FIFO)
        Queue<String> druckerQueue = new LinkedList<>();
        druckerQueue.add("Dokument1.pdf");
        druckerQueue.add("Dokument2.pdf");
        druckerQueue.add("Dokument3.pdf");
        
        System.out.println("Printer queue:");
        while (!druckerQueue.isEmpty()) {
            String dokument = druckerQueue.remove();
            System.out.println("Print: " + dokument);
        }
        
        // Performance comparison
        performanceVergleich();
    }
    
    // Bracket checking with stack
    private static boolean pruefeKlammer(String ausdruck) {
        Stack<Character> stack = new Stack<>();
        
        for (char zeichen : ausdruck.toCharArray()) {
            switch (zeichen) {
                case '(': case '[': case '{':
                    stack.push(zeichen);
                    break;
                case ')':
                    if (stack.isEmpty() || stack.pop() != '(') return false;
                    break;
                case ']':
                    if (stack.isEmpty() || stack.pop() != '[') return false;
                    break;
                case '}':
                    if (stack.isEmpty() || stack.pop() != '{') return false;
                    break;
            }
        }
        return stack.isEmpty();
    }
    
    private static void performanceVergleich() {
        final int OPERATIONEN = 1_000_000;
        
        // Stack performance
        long start = System.nanoTime();
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < OPERATIONEN; i++) {
            stack.push(i);
        }
        for (int i = 0; i < OPERATIONEN; i++) {
            stack.pop();
        }
        long stackZeit = System.nanoTime() - start;
        
        // Queue performance
        start = System.nanoTime();
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < OPERATIONEN; i++) {
            queue.add(i);
        }
        for (int i = 0; i < OPERATIONEN; i++) {
            queue.remove();
        }
        long queueZeit = System.nanoTime() - start;
        
        System.out.println("\nPerformance comparison (" + OPERATIONEN + " operations):");
        System.out.println("Stack time: " + stackZeit / 1_000_000 + " ms");
        System.out.println("Queue time: " + queueZeit / 1_000_000 + " ms");
    }
}

3. Priority Queue (Heap) Application

import java.util.*;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        // Min-heap for ascending sort
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        minHeap.add(30);
        minHeap.add(10);
        minHeap.add(20);
        minHeap.add(40);
        minHeap.add(5);
        
        System.out.println("Min-Heap (Priority Queue):");
        while (!minHeap.isEmpty()) {
            System.out.println("Extract Min: " + minHeap.remove());
        }
        
        // Max-heap with comparator
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
        maxHeap.add(30);
        maxHeap.add(10);
        maxHeap.add(20);
        maxHeap.add(40);
        maxHeap.add(5);
        
        System.out.println("\nMax-Heap:");
        while (!maxHeap.isEmpty()) {
            System.out.println("Extract Max: " + maxHeap.remove());
        }
        
        // Task scheduling with priorities
        taskSchedulingDemo();
    }
    
    private static void taskSchedulingDemo() {
        // Task with priority
        class Task {
            String name;
            int priority;
            
            Task(String name, int priority) {
                this.name = name;
                this.priority = priority;
            }
            
            @Override
            public String toString() {
                return name + " (Prio: " + priority + ")";
            }
        }
        
        // Priority queue for tasks
        PriorityQueue<Task> taskQueue = new PriorityQueue<>(
            (t1, t2) -> Integer.compare(t2.priority, t1.priority) // Max-heap
        );
        
        taskQueue.add(new Task("Send email", 2));
        taskQueue.add(new Task("Database backup", 5));
        taskQueue.add(new Task("Analyze logs", 1));
        taskQueue.add(new Task("Security update", 10));
        
        System.out.println("\nTask scheduling (high priority first):");
        while (!taskQueue.isEmpty()) {
            System.out.println("Execute: " + taskQueue.remove());
        }
    }
}
import java.util.*;

class TreeNode {
    int wert;
    TreeNode links, rechts;
    
    TreeNode(int wert) {
        this.wert = wert;
        this.links = this.rechts = null;
    }
}

class BinarySearchTree {
    TreeNode wurzel;
    
    void insert(int wert) {
        wurzel = insertRecursive(wurzel, wert);
    }
    
    private TreeNode insertRecursive(TreeNode knoten, int wert) {
        if (knoten == null) {
            return new TreeNode(wert);
        }
        
        if (wert < knoten.wert) {
            knoten.links = insertRecursive(knoten.links, wert);
        } else if (wert > knoten.wert) {
            knoten.rechts = insertRecursive(knoten.rechts, wert);
        }
        
        return knoten;
    }
    
    boolean search(int wert) {
        return searchRecursive(wurzel, wert);
    }
    
    private boolean searchRecursive(TreeNode knoten, int wert) {
        if (knoten == null) return false;
        if (knoten.wert == wert) return true;
        
        return wert < knoten.wert 
            ? searchRecursive(knoten.links, wert)
            : searchRecursive(knoten.rechts, wert);
    }
}

public class SuchbaumVsArray {
    public static void main(String[] args) {
        final int GROESSE = 100_000;
        Random random = new Random();
        
        // Build BST
        BinarySearchTree bst = new BinarySearchTree();
        for (int i = 0; i < GROESSE; i++) {
            bst.insert(random.nextInt(GROESSE * 10));
        }
        
        // Create array
        List<Integer> array = new ArrayList<>();
        for (int i = 0; i < GROESSE; i++) {
            array.add(random.nextInt(GROESSE * 10));
        }
        
        int suchZahl = random.nextInt(GROESSE * 10);
        
        // BST search O(log n) on average
        long start = System.nanoTime();
        boolean bstGefunden = bst.search(suchZahl);
        long bstZeit = System.nanoTime() - start;
        
        // Array search O(n) worst-case
        start = System.nanoTime();
        boolean arrayGefunden = array.contains(suchZahl);
        long arrayZeit = System.nanoTime() - start;
        
        System.out.println("Search for " + suchZahl + ":");
        System.out.println("BST time: " + bstZeit / 1000 + " μs, found: " + bstGefunden);
        System.out.println("Array time: " + arrayZeit / 1000 + " μs, found: " + arrayGefunden);
        
        // Sorted array (Binary Search)
        Collections.sort(array);
        start = System.nanoTime();
        int index = Collections.binarySearch(array, suchZahl);
        long binaryZeit = System.nanoTime() - start;
        
        System.out.println("Binary search time: " + binaryZeit / 1000 + " μs, index: " + index);
    }
}

5. Graph Traversal Comparison

import java.util.*;

class Graph {
    private Map<Integer, List<Integer>> adjazenzliste = new HashMap<>();
    
    void kanteHinzufuegen(int u, int v) {
        adjazenzliste.computeIfAbsent(u, k -> new ArrayList<>()).add(v);
        adjazenzliste.computeIfAbsent(v, k -> new ArrayList<>()).add(u);
    }
    
    // Breadth-First Search (Queue)
    void bfs(int start) {
        Set<Integer> besucht = new HashSet<>();
        Queue<Integer> queue = new LinkedList<>();
        
        queue.add(start);
        besucht.add(start);
        
        System.out.print("BFS: ");
        while (!queue.isEmpty()) {
            int knoten = queue.remove();
            System.out.print(knoten + " ");
            
            for (int nachbar : adjazenzliste.getOrDefault(knoten, Collections.emptyList())) {
                if (!besucht.contains(nachbar)) {
                    besucht.add(nachbar);
                    queue.add(nachbar);
                }
            }
        }
        System.out.println();
    }
    
    // Depth-First Search (Stack/Recursion)
    void dfs(int start) {
        Set<Integer> besucht = new HashSet<>();
        dfsRecursive(start, besucht);
    }
    
    private void dfsRecursive(int knoten, Set<Integer> besucht) {
        besucht.add(knoten);
        System.out.print(knoten + " ");
        
        for (int nachbar : adjazenzliste.getOrDefault(knoten, Collections.emptyList())) {
            if (!besucht.contains(nachbar)) {
                dfsRecursive(nachbar, besucht);
            }
        }
    }
}

public class GraphTraversal {
    public static void main(String[] args) {
        Graph graph = new Graph();
        
        // Build graph
        graph.kanteHinzufuegen(0, 1);
        graph.kanteHinzufuegen(0, 2);
        graph.kanteHinzufuegen(1, 3);
        graph.kanteHinzufuegen(2, 4);
        graph.kanteHinzufuegen(3, 4);
        graph.ketteHinzufuegen(4, 5);
        
        System.out.println("Graph traversal:");
        graph.bfs(0); // Level-order: 0 1 2 3 4 5
        graph.dfs(0); // Depth-first: 0 1 3 4 2 5
    }
}

Big-O Notation Comparison Table

Time Complexity

OperationArrayStackQueueHeapBSTGraph
AccessO(1)O(n)O(n)O(1)O(log n)O(V+E)
SearchO(n)O(n)O(n)O(n)O(log n)O(V+E)
InsertO(n)O(1)O(1)O(log n)O(log n)O(1)
DeleteO(n)O(1)O(1)O(log n)O(log n)O(V+E)

Space Complexity

Data StructureMemoryNote
ArrayO(n)Contiguous
StackO(n)Dynamic
QueueO(n)Circular possible
HeapO(n)Tree structure
BSTO(n)Node-based
GraphO(V+E)V=nodes, E=edges

Use Case Scenarios

Which data structure when?

Use Array when:

  • Size is known and constant
  • Frequent random access needed
  • Memory space critical (contiguous)

Use Stack when:

  • LIFO behavior needed
  • Backtracking states
  • Method calls of recursive functions

Use Queue when:

  • FIFO behavior needed
  • Implementing waiting lines
  • Breadth-First Search

Use Heap when:

  • Priorities are important
  • Frequent access to minimum/maximum
  • Scheduling algorithms

Use Tree when:

  • Storing ordered data
  • Efficient search required
  • Hierarchical relationships

Use Graph when:

  • Modeling network relationships
  • Route planning required
  • Many-to-many relationships

Performance Tips

Array Optimization

// Prepare loop
int[] array = new int[1000];
int laenge = array.length; // Not in every iteration

for (int i = 0; i < laenge; i++) {
    array[i] = i;
}

Stack Optimization

// Array instead of Stack for fixed size
int[] stack = new int[1000];
int top = -1;

void push(int wert) {
    stack[++top] = wert;
}

int pop() {
    return stack[top--];
}

Queue Optimization

// Circular Queue for fixed size
int[] queue = new int[1000];
int front = 0, rear = 0;

void enqueue(int wert) {
    queue[rear = (rear + 1) % queue.length] = wert;
}

int dequeue() {
    return queue[front = (front + 1) % queue.length];
}

Common Exam Questions

  1. Which data structure for printer queue? Queue (FIFO) - first request is processed first.

  2. Why is BST search faster than array search? BST: O(log n) by halving, Array: O(n) by linear search.

  3. What is the difference between Stack and Queue? Stack: LIFO (Last-In, First-Out), Queue: FIFO (First-In, First-Out).

  4. When do you use Heap instead of Array? When frequent access to minimum/maximum is needed (Priority Queue).

Most Important Sources

  1. https://de.wikipedia.org/wiki/Datenstruktur
  2. https://www.geeksforgeeks.org/data-structures/
  3. https://docs.oracle.com/javase/tutorial/collections/
Back to Blog
Share:

Related Posts