This tutorial will teach you how to use Algviz for algorithm visualization programming. For the complete interface definition, please refer to the Algviz API description document.
Visualizer
When using Algviz, the first object you should create is the Visualizer object. Visualization objects are used to synchronize animation frames, control animation sequences, and manage each data object to be displayed.
Create a visualizer object
When creating a visualizer object, you can specify the delay
of each animation frame and the wait
time between two adjacent animation frames. If you need to debug, you can set the wait
parameter to True
, so that the animation in Jupyter Notebook will pause until you enter a command to continue running.
import algviz
# Create a visualizer object.
# The default duration of each frame of animation is 3.0 seconds.
# The interval between two frames of animation is 0.5 seconds.
viz = algviz.Visualizer(delay=3.0, wait=0.5)
Control animation display
When the Visualizer.display interface is called, Algviz will reflect all data object changes into the animation and display it in the Jupyter Notebook.
viz.display() # Display the firt animation frame.
# Do something...
viz.display(delay=2.0) # Display the second animation frame.
Create data object
A data object is also a basic data container in Algviz (eg: vector, tree, graph). When you modify the content in the data object, Algviz will record the data changes and reflect the changes in the animation. Before using these data objects, you need to create them through the interface provided in Visualizer. Let’s learn how to create each data object!
Vector
Vector is a one-dimensional array container defined in Algviz. You can use a Vector object just like a list
object in Python.
Create a vector object
The interface Visualizer.createVector is used to create a vector object. When creating a vector object, you can specify the initial data in the vector, the name of the vector object, the cell size, and whether to display subscripts. Here is an example:
vec = viz.createVector(
data = [0, 1, 2, 3], # The initial data in Vector.
name = "Vector", # The display name of the Vector.
cell_size = (40, 40), # The cell (width, height) of the Vector.
histogram = False, # Whether to display as a histogram style.
show_index = True # Whether to display subscripts.
)
viz.display()
Modify vector elements
The elements in the vector can be accessed sequentially through the for ... in ...
iterator, and the elements at the specified positions in the vector can be accessed or modified through the []
operator.
sum = 0
# Accesse the vector elements sequentially through an iterator.
for val in vec:
sum += val
vec[0] = sum
viz.display()
vec[0] -= 1
viz.display()
Add elements to vector
- The interface Vector.append is used to add a new element to the tail of the vector.
- The interface Vector.insert is used to insert a new element into the vector at the specified position.
vec.append(0) # Adds a new element 0 at the end of the vector.
viz.display() # Render the first frame of animation.
vec.insert(index=4, val=4) # Insert element 4 before the index position 4.
viz.display(3) # Render the second frame of animation.
Mark elements in vector
- The interface Vector.mark can mark the specified element in the vector with the specified color. If the parameter
hold
is set toTrue
, the marker will not be removed in subsequent animation frames, otherwise the marker will be automatically removed in the next animation frame. - The interface Vector.removeMark is used to remove the specified color mark from the vector.
# Mark the element at position 3 in the vector green,
# the marker will be cleared on the next frame.
vec.mark(algviz.color_green, 3)
viz.display(1.0)
# All elements in the interval [0, 3) in the vector are marked as gray,
# the marker will not be cleared in later animation frames.
vec.mark(algviz.color_gray, st=1, ed=3, hold=True)
viz.display(1.0)
# Clear all gray marks.
vec.removeMark(algviz.color_gray)
viz.display(1.0)
Swap elements position
The interface Vector.swap can swap elements at two specified positions in the vector and generate the swap animation effects.
# Swap the elements at position 0 and position 4.
vec.swap(0, 5)
viz.display(3.0)
Delete elements in vector
- The interface Vector.pop removes the element at the specified position in the vector.
- Interface Vector.clear Clears all elements in a vector.
vec.pop(index=2) # Remove the element at index position 2.
viz.display(3)
vec.clear() # Clears all elements in a vector.
viz.display(3)
Table
Create a Table object
The interface Visualizer.createTable is used to create a new table object. When creating a table object, you must specify the rows and columns number of the table(you can adjust the rows and columns later). You can specify the initial data in the table, the name of the table, the length and width of the table cell, and whether to display row and column subscripts. Here is an example:
tab = viz.createTable(
row = 3, # The table has 3 rows.
col = 3, # The table has 3 columns
data = None, # The initial data in table.
name = "Table", # The name of table.
cell_size = (40, 40), # The table cell (width, height).
show_index = True # Whether to display subscripts.
)
viz.display()
Modify table elements
You can access the elements in the table by the for ... in ...
iterator. But unlike the vector
object, you need two levels of nested for ... in ...
to access specific elements in the table. The first level iterate on raws and the second level iterate on columns of each raw. Besides, you can also access the elements in the table through the [raw][col]
operator.
# Access all elements in the table through an iterator.
sum = 0
for row in tab:
for val in row:
sum += val
# Modify elements in the table with the "[]" operator.
tab[1][1] = sum
viz.display(0.5)
Mark elements in table
- The interface Table.mark can mark the specified element in the table with the given color. Similar to
Vector.mark
, if the parameterhold
is set toTrue
, the marker will not be removed in the later animation frames, otherwise, the marker will be automatically removed in the next frame; - The interface Table.removeMark is used to remove the specified color marks from the table.
# Mark the element at (row:1, col:1) as green.
tab.mark(algviz.color_green, 1, 1)
viz.display(1)
tab.removeMark(algviz.color_green)
viz.display(1)
Adjust table shape
- The interface Table.shape is used to query the current shape of the table and the return value is
(row, column)
. - The interface Table.reshape is used to change table’s shape. You can make a table become bigger or smaller.
tab.reshape(row=2, col=2) # Change the table shape to (row:2, col:2).
viz.display(1)
tab.reshape(row=2, col=3) # Change the table shape to (row:2, col:3).
viz.display(1)
Before learning the linked list
, tree
and graph
, please review the Vector
and Table
introduced earlier. Since vectors and tables are sequential storage structures, you can directly access the elements in them through vector and table objects, but linked list, tree, and graph are relational storage structures, and you cannot directly access any of them through an entry. elements.
Firstly, you need to create a SvgGraph object through the interface Visualizer.createGraph, you can learn how to use the SvgGraph
later.
linked list
Create forward linked list
The forward linked list
is connected by the ForwardLinkedListNode objects, which has a val
member and a pointer point to the next
node.
Algviz provides the parseForwardLinkedList interface to create a forward linked list
, so that there is no need to manually connect the ForwardLinkedListNode
objects one by one.
head1 = algviz.parseForwardLinkedList([0, 1, 2, 3])
forward_list = viz.createGraph(
data = head1,
name = 'Forward Linked List',
)
viz.display()
Create doubly linked list
The doubly linked list
is constructed by the DoublyLinkedListNode nodes. The DoublyLinkedListNode
has a val
member, a pointer to the predecessor node prev
and a pointer to the successor node next
.
Similarly, Algviz provides the parseDoublyLinkedList interface to create a doubly linked list
, which returns the first and last nodes of the doubly linked list.
head2, tail2 = algviz.parseDoublyLinkedList([0, 1, 2, 3])
doubly_list = viz.createGraph(
data = head2, # Just pass in the head node here.
name = 'Doubly Linked List',
)
viz.display()
Modify linked list
When a node is added or deleted in the linked list
, Algviz can automatically detect its change and generate corresponding animation.
# Delete node 1.
head1_next = head1.next
head1.next = head1_next.next
forward_list.removeNode(head1_next) # Remove the node from the SvgGraph.
viz.display()
# Add node 4.
forward_list_node = algviz.ForwardLinkedListNode(4, head1.next.next)
head1.next.next = forward_list_node
viz.display()
Tips: The SvgGraph.removeNode interface is used in the above code. This interface is used to delete an orphaned node from the topology graph, and the deleted node will not be showed in the animation.
Tree
Create binary tree
The binary tree
is connected by the BinaryTreeNode. The BinaryTreeNode
contains three members: the value[val
] of the current node, the pointer[left
] to the left subtree node and the pointer[right
] to the right subtree node.
You can construct a binary tree manually, or you can construct a binary tree through the parseBinaryTree interface.
root1 = algviz.parseBinaryTree([1, 2, 3, 4, None, 5, 6])
binary_tree = viz.createGraph(data=root1, name='Binary Tree')
viz.display()
Modify binary tree
When tree nodes or relationships between nodes change, Algviz will automatically generate corresponding animations, for example:
# Swap the left and right subtrees.
temp = root1.left
root1.left = root1.right
root1.right = temp
viz.display()
# Add a new node for tree node 2.
new_node = algviz.BinaryTreeNode('new')
root1.right.right = new_node
viz.display()
Create multi-child tree
Unlike a binary tree
, each parent node of a multi-child tree
may have multiple child nodes, so the TreeNode contains a list of child nodes, and supports the following operations to modify its child node list:
- TreeNode.children access all child nodes in the child node list iteratively.
- TreeNode.childAt returns the child node object at the given position in the child node list.
- TreeNode.childIndex returns the index of the given node in the child nodes list.
- TreeNode.add adds a new child node into the child node list.
- TreeNode.remove removes a child node from the child node list.
- TreeNode.removeAt removes the child node at the given position in the child node list.
If you want to create a multi-child tree
, you can create TreeNode
one by one and then connect them, or you can use the parseTree interface to create a multi-child tree. Here is an example:
tree_info = {
0: [1, 2, 3], # Node0 has child nodes: 1, 2, 3.
1: [4, 5], # Node1 has child nodes: 4, 5.
2: [6] # Node2 has child node: 6.
}
nodes_label = {
0: 'root', # Set the display name of node0 as 'root'.
3: 'n3' # Set the display name of node3 as 'n3'.
}
root2 = algviz.parseTree(tree_info, nodes_label)
tree = viz.createGraph(data=root2, name="Tree")
viz.display()
Modify multi-child tree
# Visit all the child nodes in root2 and mark them.
for child in root2.children():
tree.markNode(algviz.color_red, child)
viz.display(1.0)
tree_node3 = root2.childAt(2) # Record node3.
tree_node3.add(algviz.TreeNode(8)) # Add a new node8 for node3.
viz.display()
tree_node3.add(algviz.TreeNode(7), 0) # Add a new node7 for node3.
viz.display()
root2.removeAt(1) # Remove the subtree 'node2->node6' of the root node.
viz.display()
Recursive tree
The RecursiveTree can be used to show the recursive process of your algorithm. Please refer to the blog for its usage:Introduction to the RecursiveTree tool。
Graph
Create graph
The topology graph is composed of GraphNode, which contains information such as the value of the node and the list of neighbor nodes. Algviz provides the parseGraph interface for quickly creating a topology graph.
The following examples show how to create a graph object:
# Create two graph nodes n1, n2.
n1 = algviz.GraphNode(val = "n1")
n2 = algviz.GraphNode(val = "n2")
# Add two edges: (n1->n2, e1), (n2->n1, e2)
n1.add(node=n2, edge="e1")
n2.add(node=n1, edge="e2")
graph1 = viz.createGraph(
data = [n1, n2], # Bind the graph node into SvgGraph.
name = "Graph_1", # The name of the graph is 'Graph_1'.
directed = True # The graph is a directed graph.
)
viz.display()
graph_nodes = algviz.parseGraph(
nodes = [0, 1, 2], # Defines the node ID here (no duplicate values).
edges = [
(0, 1, 'a-b'), # Node 0 points to node 1, the edge shown as 'a-b'.
(1, 2, 'b-c'), # Node 1 points to node 2, the edge shown as 'b-c'.
(2, 0, 'c-a') # Node 2 points to node 0, the edge shown as 'c-a'.
],
nodes_label = {
0: 'a', # Node 0 shown as 'a'.
1: 'b', # Node 1 shown as 'b'.
2: 'c' # Node 2 shown as 'c'.
},
directed = True # The graph is a directed graph.
)
graph2 = viz.createGraph(graph_nodes, "Graph_2", True)
viz.display()
Modify graph
When the graph is modified, Algviz will automatically adjust the layout of the graph and generate corresponding animations. These are the interfaces:
- GraphNode.neighbors iterate over all neighbor nodes.
- GraphNode.neighborAt returns the node object at the index position in the neighbor list.
- GraphNode.neighborIndex returns the index position of the given node in the neighbor list.
- GraphNode.add add or insert a neighbor node into the neighbor list.
- GraphNode.remove remove a neighbor node from the neighbor list.
- GraphNode.removeAt removes the node at the specified index position among the neighbor nodes.
Here is the example:
graph_nodes[2].remove(graph_nodes[0]) # Remove the edge from node c to node a.
viz.display()
graph_node_d = algviz.GraphNode('d') # Create new node d.
graph_nodes[1].add(graph_node_d, 'b-d', 0) # Add an edge from node b to node d.
graph_node_d.add(graph_nodes[0], 'd-a') # Add an edge from node d to node a.
viz.display()
Cursor
The Cursor object can be used to indicate an index position in a vector or table. When you access an element in a vector or table by the cursor, Algviz will automatically bind the cursor to the corresponding vector or table object, and display how cursor moves.
Create a cursor object
The interface Visualizer.createCursor is used to create a cursor object, you can specify the initial position and name of the cursor when creating.
Tips: The cursor position can only be an integer, other types of values are meaningless.
i = viz.createCursor(
offset = 1, # The initial index position of the cursor is 1.
name = "i" # The name of the cursor is i.
)
j = viz.createCursor(3, "j")
After the cursor is created, it does not appear in animations by default, only when you use the cursor to access elements in a vector or table, The cursor’s position is only displayed in the associated vector or table object.
In addition, Algviz also provides Visualizer.cursorRange interface for batch creation of cursor objects, which is very useful when accessing elements iteratively.
Update the cursor positon
Once you have the cursor object, you can use it as an integer and elements in vectors and tables can be accessed through the cursor.
The cursor object supports common arithmetic operations:
+
, -
, *
, //
, %
, +=
, -=
, *=
, //=
, %=
.
The comparison operations are also supported:
>
, <
, >=
, <=
, ==
, !=
.
If you want to update the cursor position to any integer, you need to use the <<
operator.
The following example demonstrates how to use the cursor object:
Access vector element by cursor
Elements in a vector can be accessed by passing the cursor object as an argument to the vector’s []
operator.
Tips: A vector or table object can bind multiple cursor objects!
# Create a new vector object.
vec2 = viz.createVector([0, 1, 2, 3], "Vector2", show_index=True)
# Access the elements in the vector sequentially by the cursor i.
while i < len(vec2):
vec2[j] = vec2[i] + 1
viz.display(1.0)
i += 1
j -= 1
viz.display(1.0)
Access table element by cursor
i << 0 # Set the position of cursor i to 0.
(r, c) = tab.shape() # The table object is created earlier.
while i < r: # Access each row in the table iteratively.
while j < c: # Access each column in the table iteratively.
tab[i][j] = 0 # Update the currently accessed element.
viz.display(1.0)
j += 1 # Cursor j moves to the next column.
i += 1 # Cursor i moves to the next row.
j << 0 # Cursor j moves to the first column.
viz.display()
Miscellaneous
Print logs
To print the log, firstly, you need to create a Logger object through the Visualizer.createLogger interface and set the maximum number of lines in the log during initialization. Then you can call the Logger.write interface to write the information to be displayed. If the number of currently displayed log lines exceeds the limit, the oldest log will be removed automatically.
In addition, you can clear the currently displayed log at any time by calling the interface clear. Here is an example of how to print logs:
log = viz.createLogger(3)
log.write('line1')
log.write('line2')
viz.display(1.0)
log.write('line3')
viz.display(1.0)
log.write('line4')
viz.display(1.0)
log.clear()
log.write('line5')
viz.display(1.0)
Color table
When you are calling interfaces such as Vector.mark, Table.mark, and SvgGraph.markNode, you need to pass in a triple (R, G, B)
representing the RGB color value. For convenience, Algviz provides some predefined color values (eg: algviz.color_red
), the following is the color table: