A Great Visual Studio Code Extension to help you learn Data Structures Visually

Image Cover by Chinenye Okeke

I recently stumbled across a short from the official "Visual Studio Code" Channel where they showcase an extension called "Debug Visualizer". It is essentially allows you to see your Data Structures in a visual format without having yourself to imagine it. I found it to be a great way for people, like me, to practice my Data Structures and Algorithms for technical interviews because I master topics quickly when it comes to visual learning.

Trying out the tool myself!

I want to see how good it is, so I tried the extension. It was quite difficult to set up since there were limited documentation. With the help of AI, I was able to get an MVP. The example I am showcasing is reversing a Linked List:

from __future__ import annotations
from dataclasses import dataclass
from typing import Optional, Iterable, Any
import json

try:
    import debugpy  # VS Code debugging adapter
except Exception:  # pragma: no cover - defensive fallback
    debugpy = None


@dataclass
class Node:
    value: Any
    next: Optional["Node"] = None


class LinkedList:
    def __init__(self, values: Optional[Iterable[Any]] = None):
        self.head: Optional[Node] = None
        if values:
            for v in values:
                self.insert_tail(v)

    def insert_tail(self, value: Any) -> None:
        if not self.head:
            self.head = Node(value)
            return
        cur = self.head
        while cur.next:
            cur = cur.next
        cur.next = Node(value)

    def reverse_iterative(
        self,
        pause_each_step: bool = False,
        update_head_during_reverse: bool = True,
    ) -> None:
        prev, cur = None, self.head
        while cur:
            nxt = cur.next
            cur.next = prev # <-- pointer mutation (reverse one link)
            prev = cur

            # Let ll.head reflect the reversed prefix in real time (useful for visualize_ll(ll.head)).
            if update_head_during_reverse:
                self.head = prev

            # Pause so the Debug Visualizer can be refreshed on this state.
            if pause_each_step and debugpy is not None:
                debugpy.breakpoint()

            cur = nxt

        self.head = prev

    # Optional niceties for quick console inspection
    def __iter__(self):
        cur = self.head
        while cur:
            yield cur.value
            cur = cur.next

    def __repr__(self):
        return "LinkedList([" + ", ".join(map(str, self)) + "])"


def visualize_ll(head: Optional[Node]) -> str:
    nodes, edges, visited = [], [], set()
    cur = head
    while cur and id(cur) not in visited:
        visited.add(id(cur))
        nid = str(id(cur))
        nodes.append({"id": nid, "label": str(cur.value)})
        if cur.next is not None:
            edges.append({"from": nid, "to": str(id(cur.next)), "label": "next"})
        cur = cur.next

    return json.dumps({
        "kind": {"graph": True},
        "nodes": nodes,
        "edges": edges
    })


def visualize_reverse_state(prev: Optional[Node], cur: Optional[Node]) -> str:
    nodes = [
        {"id": "ENTRY_PREV", "label": "reversed head (prev)", "color": "green"},
        {"id": "ENTRY_CUR",  "label": "remaining head (cur)", "color": "orange"},
    ]
    edges = []
    visited = set()

    def add_chain(head: Optional[Node], entry_id: str, color: str):
        nonlocal nodes, edges, visited
        if head is None:
            # Show a 'None' target for clarity
            null_id = f"{entry_id}_None"
            nodes.append({"id": null_id, "label": "None", "color": color})
            edges.append({"from": entry_id, "to": null_id, "label": "head"})
            return

        edges.append({"from": entry_id, "to": str(id(head)), "label": "head"})
        cur = head
        while cur and id(cur) not in visited:
            visited.add(id(cur))
            nid = str(id(cur))
            nodes.append({"id": nid, "label": str(cur.value), "color": color})
            if cur.next is not None:
                edges.append({"from": nid, "to": str(id(cur.next)), "label": "next"})
            cur = cur.next

    add_chain(prev, "ENTRY_PREV", "green")
    add_chain(cur,  "ENTRY_CUR",  "orange")

    return json.dumps({
        "kind": {"graph": True},
        "nodes": nodes,
        "edges": edges
    })


if __name__ == "__main__":
    ll = LinkedList([3, 1, 4, 1, 5, 9])
    print("Before:", ll)
    ll.reverse_iterative(pause_each_step=True, update_head_during_reverse=True)
    print("After: ", ll)

Now the magic happens. I put the code into my Visual Studio Code to see how it would look like. Here are the steps to get it working:

  1. Make sure you "run and debug" the python file instead of simply running the file.
  2. Go to "Command Palette" and search > Debug Visualizer: New View. It will open a split window.
  3. Then, copy/paste into the field Debugging Viewer "visualize_ll(ll.head)" for this example. Then, press "continue" on the debugging section to see it change in real time!

Here is the full demonstration of it in detail:

{% embed https://www.youtube.com/shorts/3O6BFlOiFRg %}

I found this to be a great tool overall! I wish there were documentation in detail on setting it up, but it was quite limited. Initially, I thought it would be as simple as "list = [1,2,3]" but there is more to set up. If you would like to check it out the extension, here is the link for more information: Debug Visualizer for Visual Studio Code


Discussion Questions

  • What do you think of this great extension tool?
  • Have you heard of this tool before? Regardless if you have heard it or not, are you planning on using it?
  • If you used it before, what was your experience?

Question/Comments? I would love to hear from you!