Back to editor

Understanding Algorithms

A visual guide based on the book by Aditya Bhargava.

Sorting
T: O(n²)S: O(1)

Bubble Sort

Classic comparison-based sorting algorithm

Bubble Sort compares adjacent pairs and swaps those out of order. After each pass, the largest unsorted element 'bubbles' to its correct position at the end of the array.

💡 Each pass guarantees at least one more element is in its final position.

Try: change arr[j] > arr[j+1] to < to sort in descending order.

Open in editor
Sorting
T: O(n²)S: O(1)

Cocktail Sort

Bidirectional variant of Bubble Sort

Cocktail Sort (Shaker Sort) is a Bubble Sort that alternates direction each pass: left→right, then right→left. This moves both large and small elements toward their positions faster.

💡 Bidirectional scanning eliminates the 'turtle' problem — small elements near the end that Bubble Sort drags slowly.

Try: add a swap counter to compare against Bubble Sort on the same input.

Open in editor
Sorting
T: O(n²)S: O(1)

Selection Sort

Grokking Algorithms — Ch. 2: Selection Sort

Selection Sort finds the minimum in the unsorted portion and moves it to the front. Repeats until sorted. Uses very few swaps — just n-1 — but many comparisons.

💡 Minimizes memory writes — useful when writes are expensive (e.g., flash storage).

Try: swap arr[j] < arr[minIndex] with > to sort in descending order.

Open in editor
Sorting
T: O(n log n)S: O(1)

Heap Sort

Based on data structure: Max-Heap (binary tree)

Heap Sort builds a Max-Heap from the array — a binary tree where each node is larger than its children. Then repeatedly extracts the maximum, building the sorted array from back to front.

💡 The binary tree lives implicitly in the array: left child of i is at 2i+1, right child at 2i+2.

Try: modify heapify to use < instead of > and observe what happens.

Open in editor
62917384510
Sorting
T: O(n+k) médioS: O(n+k)

Bucket Sort

Distribution algorithm — divide and conquer

Bucket Sort distributes elements into buckets based on value, sorts each bucket individually, then concatenates. Works exceptionally well when values are uniformly distributed.

💡 Instead of comparing elements, the algorithm uses the value directly to decide where each element goes.

Try: change bucketCount to 3 or 10 and watch how bucket sizes change.

Open in editor
B0
B1
B2
B3
B4
Sorting
T: O(d·(n+k))S: O(n+k)

Radix Sort

Linear algorithm — sorts without direct comparisons

Radix Sort processes numbers digit by digit, from least significant (units) to most significant (thousands). Uses a stable internal sort (Counting Sort) on each pass.

💡 Never directly compares two elements — just counts and redistributes by digit. This breaks the O(n log n) lower bound of comparison-based algorithms.

Try: use an array with 3-digit numbers to watch all 3 passes of the algorithm.

Open in editor
Unidades
Sorting
T: O(n)S: O(n)

Stalin Sort

Humorous internet algorithm — O(n)

Stalin Sort traverses the array once, keeping only elements greater than or equal to the running maximum. The rest are 'eliminated'. Always produces a sorted array in O(n).

💡 Technically correct, but destroys data. Useful for understanding 'running maximum' and contrasting with Selection Sort.

Try: add logic to reject negative elements instead of non-increasing ones.

Open in editor
Graphs
T: O(V + E)S: O(V)

Breadth-First Search (BFS)

Grokking Algorithms — Ch. 6: Breadth-First Search

BFS explores a graph level by level. Starting from the source node, it visits all neighbors, then their neighbors. Uses a queue (FIFO) to control exploration order.

💡 BFS guarantees the shortest path in unweighted graphs — finds the solution with the fewest edges.

Try: add edge weights and see why BFS no longer guarantees the optimal path.

Open in editor
Graphs
T: O(V + E)S: O(V)

Depth-First Search (DFS)

Based on Grokking Algorithms Ch. 6 — depth-first variant

DFS explores as deep as possible before backtracking. Uses a stack (LIFO) — explicit or via recursion. Great for cycle detection, topological sort, and connected components.

💡 DFS doesn't guarantee shortest path, but uses less memory than BFS on wide, shallow graphs.

Try: count connected components by running DFS repeatedly from unvisited nodes.

Open in editor

How to use the editor

Write your run() function, call emitStep() for each step, then click Run. The visualizer animates every event automatically.

async function run(input) {
  const arr = [...input];

  // Emita o estado inicial
  emitStep({ t: "array", arr: [...arr] });

  for (let i = 0; i < arr.length - 1; i++) {
    // Emita cada comparação
    emitStep({ t: "compare", arr: [...arr], i, j: i + 1 });

    if (arr[i] > arr[i + 1]) {
      [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
      // Emita cada troca
      emitStep({ t: "swap", arr: [...arr], i, j: i + 1 });
    }
  }

  // Emita o estado final
  emitStep({ t: "done", arr: [...arr] });
  return arr;
}
Back to editor