How to Perform ZigZag Traversal in treelib: Complete Guide with Examples

To perform ZigZag traversal in treelib, pass mode=Tree.ZIGZAG to the expand_tree() generator method, which alternates traversal direction level-by-level using dual stacks.

The treelib library provides built-in support for alternating-level tree traversal through its Tree class implementation. By leveraging the Tree.ZIGZAG constant with the expand_tree() method, you can navigate hierarchical structures in a memory-efficient pattern that switches direction at each depth level. This guide examines the algorithm details in treelib/tree.py and provides practical implementation patterns for Python developers.

Understanding the ZigZag Implementation

Core Algorithm in expand_tree

According to the source code in treelib/tree.py (lines 52-69), the ZigZag traversal algorithm is implemented within the expand_tree() method. The implementation uses a dual-stack approach to manage the alternating traversal direction without recursion.

The algorithm follows this specific logic:

  • Initialization: Children of the current node are collected into a list called queue and reversed so the first level processes from right-to-left
  • Dual Stacks: Two stacks manage the traversal: stack_fw (forward) and stack_bw (backward)
  • Direction Switching: The algorithm tracks a boolean direction flag that flips after each level is exhausted (direction = not direction)
  • Stack Management: When the current direction is backward, child expansion lists are reversed before being pushed onto the appropriate stack

Because expand_tree() is implemented as a generator, the traversal is lazy and memory-efficient. Only nodes from the current processing level reside in memory at any given time, making this approach suitable for large tree structures regardless of total node count.

Verification in Test Suite

The behavior is validated in tests/test_tree_comprehensive.py within the test_expand_tree_zigzag_mode function. This test guarantees that the traversal starts at the root node and visits every node in the tree exactly once while maintaining the alternating-level pattern.

Performing ZigZag Traversal

Basic Usage

To execute a ZigZag traversal, create a tree structure and pass mode=Tree.ZIGZAG to the expand_tree() method. The generator yields node identifiers that you can resolve to full node objects.

from treelib import Tree

# Build a sample family tree

tree = Tree()
tree.create_node("Grandpa", "grandpa")          # root

tree.create_node("Dad",     "dad",     parent="grandpa")
tree.create_node("Mom",     "mom",     parent="grandpa")
tree.create_node("Me",      "me",      parent="dad")
tree.create_node("Sis",     "sis",     parent="dad")
tree.create_node("Cousin",  "cousin",  parent="mom")

# Execute ZigZag traversal

for node_id in tree.expand_tree(mode=Tree.ZIGZAG):
    node = tree[node_id]
    print(f"{node_id}: {node.tag}")

Expected traversal order: The algorithm visits grandpa (level 0), then alternates direction at level 1 (mom, dad), and reverses again at level 2 (cousin, sis, me). Note that within-level ordering respects the alternating direction constraint.

Filtering During Traversal

You can combine ZigZag traversal with the filter parameter to skip specific nodes and their subtrees. The filter receives a node object and must return True to include it.


# Skip nodes whose tags start with "C"

skip_c = lambda n: not n.tag.startswith("C")

for nid in tree.expand_tree(mode=Tree.ZIGZAG, filter=skip_c):
    print(tree[nid].tag)  # Excludes "Cousin" and its descendants

Custom Sorting Within Levels

Control the order of children within each level using the key, reverse, and sorting parameters. This affects how nodes are arranged before being placed on the forward or backward stacks.


# Sort by tag length, shortest first, while maintaining ZigZag pattern

for nid in tree.expand_tree(
    mode=Tree.ZIGZAG,
    key=lambda n: len(n.tag),
    sorting=True
):
    print(f"{tree[nid].tag} (length: {len(tree[nid].tag)})")

Advanced Customization Options

The expand_tree() method signature supports several optional arguments that function with Tree.ZIGZAG:

  • filter: Callable that receives a node and returns bool; False excludes the node and its entire subtree
  • key: Function used for sorting children within levels (e.g., lambda n: n.tag)
  • reverse: Boolean to reverse the sort order when sorting=True
  • sorting: Boolean flag to enable level-internal sorting before stack placement

These parameters apply to the child collection phase before the ZigZag algorithm places nodes onto the forward or backward stacks, giving you fine-grained control over traversal behavior while maintaining the alternating-level property.

Summary

  • ZigZag traversal in treelib is accessed via Tree.expand_tree(mode=Tree.ZIGZAG) in treelib/tree.py (lines 52-69).
  • The implementation uses dual stacks (stack_fw and stack_bw) to alternate direction level-by-level without recursion.
  • The method is a generator, providing memory-efficient lazy evaluation suitable for large trees.
  • Filtering and sorting parameters can be combined with ZigZag mode to customize which nodes are visited and their relative order within levels.
  • The functionality is verified by test_expand_tree_zigzag_mode in the comprehensive test suite.

Frequently Asked Questions

What is ZigZag tree traversal?

ZigZag traversal (also called spiral traversal) visits tree nodes level by level, alternating the direction of visitation at each depth. The root is visited first, then the second level is processed right-to-left, the third level left-to-right, and so on. This creates a zigzag pattern through the tree structure compared to standard breadth-first or depth-first approaches.

How does treelib's ZigZag implementation differ from standard BFS?

Unlike standard BFS which uses a single queue and always processes left-to-right, treelib's implementation in expand_tree() uses two stacks (stack_fw and stack_bw) to track the current and next levels. When the current level is exhausted, the algorithm switches stacks and reverses the processing direction, achieving the alternating pattern while maintaining O(n) time complexity with O(w) space complexity (where w is the maximum width of the tree).

Can I combine ZigZag traversal with node filtering?

Yes. Pass a filter callable to expand_tree(mode=Tree.ZIGZAG, filter=your_function) to exclude specific nodes and their subtrees. The filter function receives a node object and must return True to include that node in the traversal. This filtering occurs before the ZigZag algorithm places children onto the stacks, ensuring excluded subtrees are never processed.

Is ZigZag traversal in treelib memory efficient for large trees?

Yes. The expand_tree() method is implemented as a generator (using Python's yield keyword), making it memory efficient regardless of tree size. Only the current level's nodes are held in memory at any given time. For a tree with maximum width w, the space complexity is O(w) rather than O(n), allowing you to traverse massive trees without loading all nodes into memory simultaneously.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →