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

> Learn ZigZag traversal in treelib by passing mode=Tree.ZIGZAG to expand_tree() with dual stacks. Get a complete guide with examples for efficient tree traversal.

- Repository: [Xiaming Chen/treelib](https://github.com/caesar0301/treelib)
- Tags: how-to-guide
- Published: 2026-02-26

---

**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`](https://github.com/caesar0301/treelib/blob/main/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`](https://github.com/caesar0301/treelib/blob/main/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`](https://github.com/caesar0301/treelib/blob/main/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.

```python
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.

```python

# 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.

```python

# 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`](https://github.com/caesar0301/treelib/blob/main/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.