Visualizing algorithms with Mermaid: teaching loops and iterations
Algorithms are abstract. Reading pseudocode or code gives you the rules, but a flowchart gives you the flow—the exact path data takes through conditionals and loops. This is why whiteboard diagrams are so useful in interviews and teaching: they make the algorithm concrete. Mermaid flowcharts let you codify those diagrams so they stay version-controlled, shareable, and easy to update as the algorithm improves.
Why flowcharts for algorithms
A flowchart is a sequence of decisions and actions. That matches every algorithm:
- Initialize state
- Loop while a condition is true
- Check a condition; branch on yes/no
- Transform data
- Return or exit
For loops, flowcharts show:
- When you enter the loop
- What the loop does each iteration
- When you exit
- What happens after
For recursion, flowcharts show the base case and the recursive case side-by-side.
For sorting and searching algorithms, they reveal why the algorithm is correct.
Example 1: Binary search
Binary search is an elegant algorithm, but the invariants aren't obvious from code alone. A flowchart makes them clear:
flowchart TD
A["Start: sorted array, target value"] --> B["Initialize: left = 0, right = array.length - 1"]
B --> C{"left ≤ right?"}
C -->|No| D["Not found: return -1"]
C -->|Yes| E["mid = left + (right - left) / 2"]
E --> F{"array[mid] == target?"}
F -->|Yes| G["Found at index mid: return mid"]
F -->|No| H{"array[mid] < target?"}
H -->|Yes| I["Target is in right half: left = mid + 1"]
H -->|No| J["Target is in left half: right = mid - 1"]
I --> C
J --> C
G --> K["End"]
D --> K
style C fill:#e1f5ff
style F fill:#e1f5ff
style H fill:#e1f5ff
style K fill:#d4edda
The diagram shows the key insight: each iteration eliminates half the remaining elements. The loop condition left ≤ right ensures we exit when the range is exhausted. Recursion isn't needed—iteration is cleaner.
Example 2: Bubble sort with early termination
Bubble sort is O(n²) in the worst case, but if the array is nearly sorted, it can be much faster. Here's how to optimize it:
flowchart TD
A["Start: unsorted array"] --> B["n = array.length"]
B --> C["swapped = false"]
C --> D["for i = 0 to n-2"]
D --> E{"array[i] > array[i+1]?"}
E -->|Yes| F["Swap array[i] and array[i+1]<br/>swapped = true"]
E -->|No| G["No swap"]
F --> H["i++"]
G --> H
H --> I{"i == n-1?"}
I -->|No| E
I -->|Yes| J{"swapped == true?"}
J -->|Yes| K["n--<br/>Go to next pass"]
K --> C
J -->|No| L["Array is sorted: exit"]
L --> M["End"]
style E fill:#e1f5ff
style J fill:#e1f5ff
style M fill:#d4edda
The optimization: if no swaps happen in a pass, the array is sorted and we exit early. Without the diagram, this optimization is a detail buried in code. With it, it's the central idea.
Example 3: Merge sort (recursive divide-and-conquer)
Divide-and-conquer is harder to visualize, but flowcharts handle recursion well:
flowchart TD
A["Start: unsorted array"] --> B{"Is array.length ≤ 1?"}
B -->|Yes| C["Base case: already sorted<br/>return array"]
B -->|No| D["mid = array.length / 2"]
D --> E["left = mergeSort<br/>array[0...mid]"]
E --> F["right = mergeSort<br/>array[mid...end]"]
F --> G["merged = merge left and right"]
G --> H["return merged"]
C --> I["End"]
H --> I
subgraph merge["Merge Subroutine"]
M1["Compare heads of left and right"] --> M2{"Left head < right head?"}
M2 -->|Yes| M3["Take from left"]
M2 -->|No| M4["Take from right"]
M3 --> M5["Add to result<br/>Move left pointer"]
M4 --> M6["Add to result<br/>Move right pointer"]
M5 --> M7{"Both lists empty?"}
M6 --> M7
M7 -->|No| M1
M7 -->|Yes| M8["Append remaining<br/>return result"]
end
G --> merge
merge --> H
style B fill:#e1f5ff
style C fill:#fff3e0
style M2 fill:#e1f5ff
The diagram separates the divide step (recursively sort left and right halves) from the merge step (combine two sorted arrays). This structure is the entire algorithm. Code is just the implementation.
Teaching with loop iterations
When teaching loops, show how state changes with each iteration. Here's a simple example: sum an array.
flowchart TD
A["Array: [2, 4, 6, 8]"] --> B["Initialize: sum = 0, i = 0"]
B --> C{"i < 4?"}
C -->|No| E["return sum"]
C -->|Yes| D["sum = sum + array[i]<br/>i = i + 1"]
D --> C
subgraph iter1["Iteration 1"]
direction LR
I1a["sum = 0, i = 0<br/>Check: 0 < 4? YES"] --> I1b["sum = 0 + 2 = 2<br/>i = 1"]
end
subgraph iter2["Iteration 2"]
direction LR
I2a["sum = 2, i = 1<br/>Check: 1 < 4? YES"] --> I2b["sum = 2 + 4 = 6<br/>i = 2"]
end
subgraph iter3["Iteration 3"]
direction LR
I3a["sum = 6, i = 2<br/>Check: 2 < 4? YES"] --> I3b["sum = 6 + 6 = 12<br/>i = 3"]
end
subgraph iter4["Iteration 4"]
direction LR
I4a["sum = 12, i = 3<br/>Check: 3 < 4? YES"] --> I4b["sum = 12 + 8 = 20<br/>i = 4"]
end
subgraph iter5["Exit"]
direction LR
I5a["sum = 20, i = 4<br/>Check: 4 < 4? NO"] --> I5b["return 20"]
end
B --> iter1
iter1 --> iter2
iter2 --> iter3
iter3 --> iter4
iter4 --> iter5
iter5 --> E
style C fill:#e1f5ff
By showing each iteration explicitly, students see:
- How the loop variable changes
- How state accumulates
- When the loop exits
Handling edge cases and errors
Good algorithms handle edge cases. Use flowcharts to document them:
flowchart TD
A["Start: Search for value in array"] --> B{"Is array empty?"}
B -->|Yes| C["Edge case:<br/>No elements to search<br/>return -1"]
B -->|No| D{"Is value null?"}
D -->|Yes| E["Error case:<br/>Invalid input<br/>return -1"]
D -->|No| F["Proceed with search"]
F --> G["for i = 0 to array.length - 1"]
G --> H{"array[i] == value?"}
H -->|Yes| I["return i"]
H -->|No| J["i++"]
J --> K{"i == array.length?"}
K -->|No| H
K -->|Yes| L["Value not found<br/>return -1"]
C --> M["End"]
E --> M
I --> M
L --> M
style B fill:#ffe0b2
style D fill:#ffe0b2
style C fill:#ffccbc
style E fill:#ffccbc
The color-coding (orange for edge cases, red for errors) makes them stand out. This is valuable for code review: "did we handle all the edge cases?"
Tips for algorithm flowcharts
-
Use color coding:
- Decisions (diamonds): light blue
- Base cases / termination: light green
- Edge cases: orange
- Errors: light red
-
Label decisions clearly. Instead of "Is n even?", write the actual condition:
n % 2 == 0? -
Show state changes explicitly.
x = x + 1vs justx++. Make the transformation visible. -
Separate loops from main logic. Use subgraphs for the loop body if it's complex.
-
Document invariants as notes. If the algorithm depends on a sorted array, an increasing counter, or a decreasing range, add a note explaining why.
-
Compare implementations. Diagram both your original algorithm and a candidate optimization. The flowchart makes it obvious which paths differ.
FAQ
Should I include every line of code in the flowchart?
No—only the control flow. Assignment statements that don't affect decisions can be grouped. The point is to see which paths the algorithm takes, not to transcribe code.
Can I use flowcharts for pseudocode teaching?
Absolutely. Flowcharts are language-agnostic. Whether you're teaching Python, Java, or C++, the flowchart is the same. It's the algorithm, not the syntax.
How do I diagram recursion?
Show the recursive call as a separate node or subgraph. Include the base case and the recursive case clearly. A simple diagram is better than trying to show the entire call stack—the point is to understand the branching logic, not trace every call.
Is it better to have one big flowchart or many small ones?
Many small ones. One flowchart per algorithm or per subroutine. If it doesn't fit on a screen, it's too big—split it into helper functions and link them.
Map your algorithm to a flowchart in the MermaidCreator editor. Walk through each decision and loop, trace a few test cases, and verify the logic before you code it.
Related posts
Mermaid graph diagrams for data structures
Visualize graphs, trees, and complex data structures with Mermaid. Learn syntax, real-world examples, and best practices for algorithm documentation.
Flowchart documentation best practices for complex systems
Master the art of documenting complex systems with Mermaid flowcharts. Avoid common pitfalls and write flowcharts that teams actually understand.