# 算法可视化面板使用说明

Visualize Your Code

The visual panel editor is now available online and can be accessed via:

https://labuladong.online/algo-visualize/

**Additionally, an 'Edit' button has been added to the algorithm visualization panels embedded in this site and all related plugins. Clicking it allows you to directly modify and execute your code for visualization**.

Currently, the visualization panel only supports JavaScript code. If you find it difficult to understand the JavaScript code in the visualization panel, I have written a Minimal JavaScript Tutorial specifically for using the visualization panel, which will help you get started in 5 minutes.

I hope you read this article to the end, because in the last part, I will teach you practical techniques for using the visualization panel in specific algorithm problem scenarios, guiding you step-by-step to efficiently analyze problems and the execution process of algorithms. Once you recognize the power of the visualization panel, you will find that it greatly enhances your understanding of algorithms.

## Basic Usage

**At the top** are mainly control buttons and sliders, which can control the step-by-step execution of the code.

**On the left** is the code panel, where the currently executing line of code is highlighted. **Clicking on the code can directly run it to the specified location**. After clicking the 'Edit' button at the top, the left code panel turns into an editor, allowing you to directly modify and run the code.

Most Useful Technique

Clicking on the code to run it to a specific location is the most commonly used feature. This function is similar to the 'Run to Cursor' feature in an IDE when debugging code.

Usually, we don't start from the beginning and step through the algorithm execution one by one, but rather click on the code to jump to an interesting location and then step through it to observe the execution process.

**On the right** is the visualization area, which displays variables, data structures, stack information, recursion counts, etc. Hovering over a data structure allows you to view detailed information.

**In the bottom left** is the Log Console. If your code uses `console.log`

, the output will be printed here. The `console.log`

function has been enhanced to **automatically add indentation to the output content based on the recursion depth**, making it easier to observe the recursive process. You can take a close look at the output in the Log Console in the above image.

**In the bottom right** are some floating buttons that provide functions such as refreshing the panel, full-screen display, and showing/hiding the Log Console.

I recorded a video on Bilibili explaining and demonstrating the basic functions mentioned above. If you're interested, you can check it out:

Understanding these is enough to get you started with the visualization panel. You can play around with these features on the two panels.

The following content mainly covers the functions of the visual editor for executing your own code.

## Enhanced `console.log`

A long time ago, I shared an article on Test-taking "Cheating" Techniques, where I introduced a trick for debugging recursive algorithms using only standard output during written exams. Therefore, I enhanced the `console.log`

function (JavaScript's print output function) in the visualization panel to help everyone observe the output of complex algorithms more intuitively.

The enhancements are as follows:

If you use

`console.log`

in the visualization panel, the output will be printed in the Log Console at the bottom left, and it will automatically add indentation based on the recursion depth.Clicking on the output in the console will mark all outputs with the same indentation with a dashed line, making it easier to understand which outputs are at the same recursion depth.

If you don't want to use automatic indentation, you can use the

`console._log`

method. This method is the original print output function and does not automatically add indentation.

Enough talk, let's see this panel:

The code outputs at the start and end of recursion. You can see that the console at the bottom left displays the following content, with outputs from recursive functions at the same level having the same indentation, making it easy to distinguish:

```
进入 traverse(1)
进入 traverse(2)
进入 traverse(3)
进入 traverse(4)
退出 traverse(4)
退出 traverse(3)
退出 traverse(2)
退出 traverse(1)
```

You can click the first character "进" or "退" in each line of output in the console. This will show a dashed line aligning all outputs with the same indentation, making it easier to see that "Entering traverse(1)" corresponds to "Exiting traverse(1)", "Entering traverse(2)" corresponds to "Exiting traverse(2)", and so on.

If you don't want to use the automatic indentation feature, you can change `console.log`

in your code to `console._log`

. This way, the output won't be automatically indented. Feel free to modify the code and try it out.

## Visualizing Data Structures

The panel can visualize common data structures such as arrays, linked lists, binary trees, hash tables, and hash sets. Below is a detailed introduction on how to create these data structures.

### Standard Library Data Structures

For JavaScript built-in data structures like arrays and hash tables, you can initialize and operate them as usual, for example:

What needs special attention are some specific data structures in LeetCode, such as the singly linked list `ListNode`

and the binary tree `TreeNode`

. Their basic definitions and usage are the same as on LeetCode. Here are some examples.

### Singly Linked List

First, let's talk about the singly linked list. Each node in a singly linked list has `val`

and `next`

attributes, which are exactly the same as defined on LeetCode:

```
// Definition of a singly linked list node
// This type is built-in and can be used directly without re-declaration
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
```

You can use the `ListNode.create`

method to quickly create a singly linked list. Here's a simple example:

### Doubly Linked List

Each node in a doubly linked list has `val, prev, next`

attributes. The constructor is exactly the same as on LeetCode:

```
// Definition of a doubly linked list node
// This type is built-in and can be used directly without needing to declare it again
function DoubleListNode(val, prev, next) {
this.val = (val===undefined ? 0 : val)
this.prev = (prev===undefined ? null : prev)
this.next = (next===undefined ? null : next)
}
```

You can use the `DoubleListNode.create`

method to quickly create a doubly linked list, or you can manually assemble it. Here are some simple examples:

### Binary Trees

Now let's talk about binary trees. Each node in a binary tree has `val, left, right`

attributes, and the constructor is consistent with LeetCode:

```
// Definition of a binary tree node
// This type is built-in and can be used directly without further declaration
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
```

You can use the `TreeNode.create`

method to quickly create a binary tree. Note that we use lists to represent binary trees, in the same way as how LeetCode represents binary trees. Here are two examples:

Each binary tree node has `val, left, right`

attributes, which are exactly the same as the definitions on LeetCode.

### N-ary Tree

Each node in an N-ary tree has `val, children`

attributes. The constructor is identical to LeetCode's definition:

```
// Definition of a multi-way tree node
// This type is built-in and can be used directly without needing to declare it again
function Node(val, children) {
this.val = val;
this.children = children;
}
```

You can use `Node.create`

to create a multi-way tree. Pay attention to the representation method of multi-way trees. We still use lists to represent multi-way trees, in the order of level-order traversal, and use `null`

to separate each group of child nodes. For reference, you can check the LeetCode problem 429. N-ary Tree Level Order Traversal for examples.

Each multi-way tree node has `val, children`

attributes, which are exactly the same as the definitions on LeetCode.

### Binary Heap (Priority Queue)

In my article Binary Heap Basics and Implementation, I explained the basic concepts of binary heaps, the code implementation of priority queues, and the core operations of floating up (`swim`

) and sinking down (`sink`

) using a visual panel.

Tip

For binary heaps (priority queues), the main APIs are the `push, pop, peek`

methods, which are used to insert elements and remove the top element of the heap.

In Binary Heap Basics and Implementation, I also used methods like `_swim, _sink, _getLastNode, _makeNode`

, but these are internal helper methods for binary heaps and are only used for teaching purposes, so they won't be introduced here.

The visual panel provides some shortcut methods to create max heaps and min heaps, and also supports using custom comparison functions to create binary heaps. Here are some examples:

## Variable Hiding and Scope Promotion

Using `@visualize global`

can elevate a variable's scope to global, allowing some closure variables to remain visible in the right-side visualization panel.

Using `@visualize hide`

can hide variables, enabling you to conceal irrelevant data structures and prevent overcrowding in the visualization panel.

Let's illustrate the usage of these comments with an example. Suppose I want to implement a simple `Stack`

class with methods `push, pop, peek`

. I can leverage JavaScript function closures like this:

However, if you scroll to the last step, you'll notice that the `stack`

variable persists as an Object in the visualization panel, which is not meaningful and takes up a lot of space.

What we're more interested in is the `items`

variable, as we can observe the changes in this array to understand how the `push, pop`

methods work.

Since `items`

is declared within the `Stack`

function, it is not in the current scope when the code runs outside the function body, and thus it won't be visualized in the right panel.

**To address this, we can use the @visualize global comment to promote the items variable to the global scope, and then use the @visualize hide comment to hide the stack variable in the right panel**.

This achieves the desired effect. Click on the line `stack.push(1);`

and execute downwards. You'll see that the `stack`

variable no longer appears, and the update process of the `items`

array is displayed on the right:

## Visualizing the Backtracking/DFS Recursive Process

Tip

Beginners can skip this section.

This content will be useful when you have studied the binary tree chapter or the dynamic programming/backtracking chapter, and need to visualize your recursive functions.

Recursive algorithms can be a headache for many readers. I previously wrote an article Understanding All Recursive Algorithms from a Tree Perspective, which theoretically abstracts the most basic thinking model and writing techniques for recursive algorithms.

Simply put: **Think of the recursive function as a pointer on a recursive tree. Backtracking algorithms traverse a multi-way tree and collect the values of leaf nodes; dynamic programming decomposes problems and uses the answers to subproblems to derive the answer to the original problem**.

Now, I have integrated the thinking model described in the article into the algorithm visualization panel, which will definitely help you understand the execution process of recursive algorithms more intuitively.

For recursive algorithms, comments like `@visualize status`

, `@visualize choose`

, and `@visualize unchoose`

can be very helpful. Let's go through them one by one.

### Example of `@visualize status`

**In a nutshell, the @visualize status comment can be placed above a recursive function that needs visualization, to control the values of nodes in the recursive tree.**

Let's look at a simple example. When explaining the Core Framework of Dynamic Programming, I drew the recursive tree for the Fibonacci problem, showing how larger problems at the top are gradually broken down:

How do we describe the process of the algorithm running? The recursive function `fib`

acts like a pointer on the recursive tree, continuously breaking down the original problem into subproblems. When the problem is broken down to the base case (leaf nodes), it starts returning the answers of subproblems layer by layer, deducing the answer to the original problem.

With the `@visualize status`

comment, this process can be seen very intuitively. The following panel shows the 27th step of the Fibonacci algorithm, attempting to calculate the value of `fib(3)`

:

The `@visualize status`

comment is written above the `fib`

function, and its specific meanings are:

Enable the recursive tree visualization for this

`fib`

function. Each recursive call will be visualized as a node on the recursive tree, and the value of`n`

in the function parameters will be displayed as the state on the node.The

`fib`

function is regarded as a pointer traversing this recursive tree, and the branches on the stack path will be highlighted in bold.If the function has a return value, when the function ends and calculates the return value of a node, moving the mouse over this node will display the return value.

Practice

Please enter 27 in the step bar and press Enter to jump to the 27th step. Move the mouse over the node `(2)`

, and you can see `fib(2) = 1`

, indicating that the value of `fib(2)`

has been calculated.

Nodes `(5), (4), (3)`

on the recursive path have not been calculated yet, and moving the mouse over them will not display any return value.

Practice

Try clicking the forward and backward buttons on the visualization panel to let the algorithm run a few steps forward, understanding the growth process of the recursive tree in dynamic programming.

Practice

When the entire recursive tree is traversed, the original problem `fib(5)`

will be calculated. At that point, all nodes on the recursive tree will display return values.

Try dragging the progress bar to the end to see what the recursive tree looks like, and move the mouse over various nodes to see what is displayed.

The Fibonacci solution code shown earlier did not include a memoization, making it an exponential time complexity algorithm. Now let's see what impact adding memoization has on the growth of the entire recursive tree.

Practice

👇 Please drag the progress bar to the end of the algorithm to see what the recursive tree looks like.

Practice

👇 This is a version with memoization optimization. Please drag the progress bar to the end of the algorithm to see what the recursive tree looks like, and compare it with the recursive tree without memoization.

By visualizing the entire tree, can you now intuitively understand how dynamic programming eliminates overlapping subproblems through memoization?

### Example of `@visualize choose/unchoose`

**In a nutshell, @visualize choose/unchoose comments are placed before and after recursive function calls to control the values on the branches of the recursion tree.**

Let's create a simple backtracking problem. Suppose you need to write a `generateBinaryNumber`

function to generate binary numbers of length `n`

. For example, when `n = 3`

, it should generate the 8 binary numbers: `000, 001, 010, 011, 100, 101, 110, 111`

.

The solution code for this problem is not complicated. Let me show you how to use `@visualize choose/unchoose`

comments to visualize the backtracking process:

You can move your mouse over any node in the recursion tree to display all the information on the path from the root node to that node.

Doesn't it make understanding the recursive algorithm code much more intuitive when you combine it with this recursion tree?

## Visualizing the BFS Process

As mentioned in Binary Tree Thinking (Overview), the BFS algorithm is an extension of level-order traversal of binary trees.

Since recursion trees can be visualized, the BFS process can also be visualized. You can use `@visualize bfs`

to enable automatic generation of the exhaustive tree for BFS algorithms. The values on the nodes represent the elements in the queue, and `@visualize choose/unchoose`

comments can control the values on the branches.

Here's a simple example to demonstrate the visualization of the BFS process:

## Scenario Practice Questions

Next, I will ask you questions based on specific algorithm problems, requiring you to efficiently analyze the problem and the algorithm's execution process using the visualization panel. I will provide the answers, so you can think about them first and then see my solutions.

This section primarily uses the "Execute to Specific Code Position" feature of the visualization panel: you can click on a part of the code, and the algorithm will execute up to that point, visualizing the process. **Using this feature well can greatly enhance your understanding of algorithms**.

### Warm-Up

First, let's get familiar with the "Execute to Specific Code Position" feature. In the previously demonstrated linked list cycle detection algorithm, I already guided you through using it. I asked you to repeatedly click on the line `if (slow === fast)`

to observe the chasing process of the fast and slow pointers:

Why does clicking this part show the chasing of the fast and slow pointers? Because when this part of the code is executed, the `fast`

and `slow`

pointers have just moved, so clicking here allows you to see their complete movement process.

In the following exercises, please focus on thinking about the state of the algorithm when the code execution reaches a certain part. This way, you can better use the visualization panel to precisely control the execution of the algorithm.

### Scenario One: Preorder, Inorder, and Postorder Traversal of a Binary Tree

Hands-On

The code above demonstrates the preorder, inorder, and postorder traversal of a binary tree, which should be familiar to everyone.

Please click the play button to view the execution flow of this code. During playback, the previous/next step buttons will turn into speed up/slow down buttons to adjust the playback speed.

Questions

You can see that the binary tree will be visualized, and the `root`

variable acts like a pointer moving through the tree. I want you to use the "Execute to Specific Code Position" feature to click on the appropriate places in the code to precisely control the movement order of the `root`

pointer.

Reset the panel and make the

`root`

pointer traverse the entire binary tree in preorder, i.e., in the sequence`1,2,4,3,5,6`

.Reset the panel and make the

`root`

pointer traverse the entire binary tree in inorder, i.e., in the sequence`2,4,1,5,3,6`

.Reset the panel and make the

`root`

pointer traverse the entire binary tree in postorder, i.e., in the sequence`4,2,5,6,3,1`

.

## Answers

These questions are not difficult:

Repeatedly click on the part of the code

`preorderResult.push(root.val)`

.Repeatedly click on the part of the code

`inorderResult.push(root.val)`

.Repeatedly click on the part of the code

`postorderResult.push(root.val)`

.

### Scenario Two: Observing the Growth of the Recursive Tree

Question

👇 This is the Fibonacci algorithm without memoization that we just discussed. Now, based on your understanding of recursive trees, click the appropriate location in the code so that with each click, the recursive tree grows by one node, until the recursive tree is fully formed.

## Answer

You can continuously click on the line `if (n < 2)`

in the code to observe the growth process of the recursive tree.

Why does clicking this part show the growth process of the recursive tree? Because this part of the code is the base case of the recursive function, which is executed in every recursive call. Each node in the recursive tree corresponds to each recursive call, so clicking this if statement allows you to see the growth process of the nodes in the recursive tree.

This is a very commonly used technique to observe the recursive process.

### Scenario Three: Quickly Obtain an Exhaustive Result

Question One

👇 This is the visualization panel for the permutation algorithm shown in the previous text. In Scenario Two, you observed the growth of the recursive tree for the Fibonacci problem. Now, please click on the appropriate location in the code to observe the growth of the recursive tree for the permutation problem.

## Answer One

This is simple. Just keep clicking on the `if (track.length === nums.length)`

part of the code to observe the growth of the recursive tree.

Question Two

Reset the panel.

Now, I don't want to see the recursive tree grow node by node; it's not very interesting. I want you to click on the appropriate location in the code so that each click grows a "branch" of the recursive tree.

This is a common scenario in backtracking algorithms because the end of each "branch" is a leaf node, and leaf nodes usually store one of the exhaustive results generated by the backtracking algorithm. For example, in this problem, the leaf node at the end of each branch represents a permutation result.

## Answer Two

Keep clicking on the `res.push([...track])`

part of the code. Each click will cause the backtracking tree to grow a "branch."

Because leaf nodes are essentially the nodes where the recursive tree stops growing, i.e., the base case of the `backtrack`

function. So, by clicking on the code in the base case, we can directly jump to the leaf nodes of the recursive tree, which is equivalent to growing a branch.

### Scenario Four: Understanding Top-Down and Bottom-Up Thinking Patterns

Question

In my article Dynamic Programming Core Framework, I explained that the top-down recursive approach using a `memo`

memoization and the bottom-up iterative approach using a `dp`

array are essentially the same. The values in the `dp`

array and the `memo`

memoization are identical, and the two methods can be converted into each other.

Below, I will provide you with both the top-down recursive solution and the bottom-up iterative solution. Please complete the following tasks:

In the panel for the top-down recursive solution, click on the appropriate location in the code so that each click updates an element in the

`memo`

.In the panel for the bottom-up iterative solution, click on the appropriate location in the code so that each click updates an element in the

`dp`

.Compare the updates of the

`dp`

array and the`memo`

array in both solutions to understand that the top-down recursive solution and the bottom-up iterative solution are essentially the same.

## Answer

In the panel for the top-down recursive solution, each click on the code segment

`memo[n] = dp(memo, n - 1) + dp(memo, n - 2)`

will update an element in the`memo`

array.In the panel for the bottom-up iterative solution, each click on the code segment

`dp[i] = dp[i - 1] + dp[i - 2]`

will update an element in the`dp`

array.It should be easy to understand with the help of the visualization panels. However, you might notice that the values of

`memo[1]`

and`dp[1]`

are different. This is because we set`dp[1]`

as a base case with an initial value, whereas in the recursive solution, the base case is directly returned and does not need to be set in the`memo`

.