How to Perform Depth-First Search (DFS) Traversal in treelib: A Complete Guide
Use Tree.expand_tree() with mode=Tree.DEPTH (or rely on the default) to yield node identifiers in pre-order DFS sequence.
The treelib library provides a lightweight Python implementation for general-purpose tree data structures. When you need to visit every node in a tree by diving deep into branches before exploring siblings, the Tree class in treelib/tree.py offers a built-in generator that handles Depth-First Search (DFS) traversal efficiently.
Understanding DFS Architecture in treelib
The Tree Class and Traversal Constants
In treelib/tree.py, the Tree class defines four traversal mode constants at line 135:
| Constant | Value | Description |
|---|---|---|
Tree.ROOT |
0 | Root node only |
Tree.DEPTH |
1 | Depth-first search (pre-order) |
Tree.WIDTH |
2 | Breadth-first search (level-order) |
Tree.ZIGZAG |
3 | Alternating left-right per level |
When performing DFS traversal in treelib, you reference Tree.DEPTH to explicitly request depth-first behavior.
How expand_tree Implements DFS
The core traversal logic resides in Tree.expand_tree() starting at line 929 in treelib/tree.py. This method is a generator that yields node identifiers (strings) according to the specified mode.
When mode=Tree.DEPTH (or when no mode is specified, since DEPTH is the default), the implementation uses a queue-based approach that simulates recursion:
- It initializes an
expansionlist with the starting node (defaulting to the tree root) - For each iteration, it pops the first element and yields its identifier
- It retrieves the node's children via
tree.children(nid) - It concatenates these children in front of the remaining queue:
queue = expansion + queue[1:]
This prepending operation at line 1047 produces a pre-order DFS traversal—visiting a node before its children, and exploring the first child deeply before moving to siblings.
Performing DFS Traversal
Basic DFS Using the Default Iterator
Since Tree.DEPTH is the default mode for expand_tree(), you can perform DFS traversal with minimal code:
from treelib import Tree
# Construct a sample tree
tree = Tree()
tree.create_node("Root", "root")
tree.create_node("A", "a", parent="root")
tree.create_node("B", "b", parent="root")
tree.create_node("C", "c", parent="a")
tree.create_node("D", "d", parent="a")
tree.create_node("E", "e", parent="b")
# DFS traversal (pre-order) - default behavior
print("DFS traversal order:")
for node_id in tree.expand_tree():
node = tree[node_id]
print(f" {node_id}: {node.tag}")
Output:
DFS traversal order:
root: Root
a: A
c: C
d: D
b: B
e: E
The generator yields identifiers in pre-order DFS sequence: root, then A and its descendants (C, D), then B and its descendant (E).
Explicit DFS Mode Selection
For code clarity, explicitly pass Tree.DEPTH to expand_tree():
# Explicit DFS mode
for node_id in tree.expand_tree(mode=Tree.DEPTH):
print(tree[node_id].tag)
This produces identical output to the default call but documents the traversal strategy for future maintainers.
Manual Recursive DFS
When you need custom state tracking or post-order processing, implement recursive DFS using Tree.children():
def dfs_recursive(tree, node_id, visit_callback):
"""
Perform recursive DFS on treelib Tree.
visit_callback receives the Node object.
"""
node = tree[node_id]
visit_callback(node) # Pre-order visit
# Recurse on children (returns list of Node objects)
for child in tree.children(node_id):
dfs_recursive(tree, child.identifier, visit_callback)
# Usage
def print_node(node):
indent = " " * tree.level(node.identifier)
print(f"{indent}{node.tag}")
dfs_recursive(tree, tree.root, print_node)
This approach gives you full control over the traversal stack and enables post-order or in-order variations by moving the visit_callback invocation.
Practical Applications
Calculating Tree Depth
Combine DFS traversal with Tree.depth() to analyze tree structure. The depth() method at line 778 in treelib/tree.py computes either the maximum depth of the entire tree or the level of a specific node:
# Maximum depth from root to deepest leaf
max_depth = tree.depth()
print(f"Tree depth: {max_depth}")
# Level of specific node (distance from root)
level_of_c = tree.depth("c")
print(f"Node 'c' is at level: {level_of_c}")
Processing Nodes with Custom Logic
DFS traversal enables tree reduction operations, such as summing values stored in node data:
# Assume nodes have numeric data
tree = Tree()
tree.create_node("Root", "root", data=10)
tree.create_node("Child1", "c1", parent="root", data=5)
tree.create_node("Child2", "c2", parent="root", data=3)
total = 0
for nid in tree.expand_tree():
node = tree[nid]
if node.data:
total += node.data
print(f"Sum of all node data: {total}") # Output: 18
Summary
Tree.expand_tree()is the primary method for DFS traversal in treelib, defaulting toTree.DEPTHmode for pre-order depth-first search.- The implementation in
treelib/tree.pyuses a queue-based generator that prepends children to the expansion list, yielding identifiers in root-left-right sequence. - Access actual Node objects by indexing the tree with the identifier:
tree[node_id]. - For custom traversal logic, use
Tree.children()to retrieve child nodes and implement recursive DFS manually. - Combine traversal with
Tree.depth()to analyze tree levels and structure.
Frequently Asked Questions
What is the default traversal mode in treelib?
By default, Tree.expand_tree() uses Tree.DEPTH (value 1), which performs a pre-order depth-first search. This means it visits the root node, then recursively explores each branch fully before moving to the next sibling. You can verify this in the method signature at line 929 of treelib/tree.py.
How do I perform post-order DFS traversal in treelib?
The built-in expand_tree() only supports pre-order DFS. For post-order traversal (children before parent), implement a recursive function using Tree.children(). Process all children recursively before visiting the current node, as shown in the manual recursive DFS example above.
Can I limit DFS traversal to a specific subtree?
Yes. Pass the nid parameter to expand_tree() to specify the starting node identifier. For example, tree.expand_tree(nid="a") performs DFS starting from node "a" rather than the tree root. This is useful for processing specific branches without traversing the entire tree.
What is the difference between Tree.DEPTH and Tree.WIDTH?
Tree.DEPTH (value 1) performs depth-first search, exploring as far as possible along each branch before backtracking. Tree.WIDTH (value 2) performs breadth-first search, visiting all nodes at the current level before moving to the next level. Both modes are available in expand_tree() via the mode parameter.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →