ZigZag Tree Traversal in C++ (Time Complexity: O(n))


Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).

Example:

Input: root = [3, 9, 20, null, null, 15, 7]
    3
   / \
  9  20
    /  \
   15   7

Output: 
[
  [3],
  [20,9],
  [15,7]
]

Understanding the Problem

The core challenge of this problem is to traverse the binary tree in a zigzag manner. This means that for each level of the tree, the traversal direction alternates between left-to-right and right-to-left. This type of traversal is significant in scenarios where hierarchical data needs to be processed in a non-linear order.

Approach

To solve this problem, we can use a breadth-first search (BFS) approach with a queue to traverse the tree level by level. We will also use a flag to keep track of the current direction of traversal (left-to-right or right-to-left). For each level, we will collect the nodes' values and reverse the order if the current direction is right-to-left.

Naive Solution

A naive solution would involve traversing the tree level by level and then reversing the order of nodes for every alternate level. However, this approach is not optimal as reversing the list at each level adds unnecessary complexity.

Optimized Solution

An optimized solution involves using a deque (double-ended queue) to efficiently add nodes from both ends based on the current traversal direction. This avoids the need to reverse the list at each level.

Algorithm

  1. Initialize a queue and add the root node to it.
  2. Initialize a flag to keep track of the current traversal direction.
  3. While the queue is not empty, do the following:
    • Get the number of nodes at the current level.
    • Initialize an empty deque to store the nodes' values for the current level.
    • For each node at the current level, do the following:
      • Remove the node from the queue.
      • If the current direction is left-to-right, add the node's value to the end of the deque.
      • If the current direction is right-to-left, add the node's value to the front of the deque.
      • Add the node's children to the queue.
    • Convert the deque to a list and add it to the result.
    • Toggle the traversal direction.

Code Implementation

#include <iostream>
#include <vector>
#include <deque>
#include <queue>

using namespace std;

// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
    vector<vector<int>> result;
    if (!root) return result;

    queue<TreeNode*> nodeQueue;
    nodeQueue.push(root);
    bool leftToRight = true;

    while (!nodeQueue.empty()) {
        int levelSize = nodeQueue.size();
        deque<int> levelNodes;

        for (int i = 0; i < levelSize; ++i) {
            TreeNode* currentNode = nodeQueue.front();
            nodeQueue.pop();

            if (leftToRight) {
                levelNodes.push_back(currentNode->val);
            } else {
                levelNodes.push_front(currentNode->val);
            }

            if (currentNode->left) nodeQueue.push(currentNode->left);
            if (currentNode->right) nodeQueue.push(currentNode->right);
        }

        result.push_back(vector<int>(levelNodes.begin(), levelNodes.end()));
        leftToRight = !leftToRight;
    }

    return result;
}

// Helper function to create a new tree node
TreeNode* newNode(int data) {
    TreeNode* node = new TreeNode(data);
    node->left = node->right = NULL;
    return node;
}

// Main function to test the zigzag traversal
int main() {
    TreeNode* root = newNode(3);
    root->left = newNode(9);
    root->right = newNode(20);
    root->right->left = newNode(15);
    root->right->right = newNode(7);

    vector<vector<int>> result = zigzagLevelOrder(root);

    for (const auto& level : result) {
        for (int val : level) {
            cout << val << " ";
        }
        cout << endl;
    }

    return 0;
}

Complexity Analysis

The time complexity of this solution is O(n), where n is the number of nodes in the binary tree. This is because each node is processed exactly once. The space complexity is also O(n) due to the storage required for the result and the queue used for BFS.

Edge Cases

Some potential edge cases include:

These cases are handled by the algorithm as it checks for null root and processes each node individually.

Testing

To test the solution comprehensively, consider the following test cases:

Use a variety of test cases to ensure the solution handles all scenarios correctly.

Thinking and Problem-Solving Tips

When approaching such problems, consider the following tips:

Conclusion

In this blog post, we discussed the zigzag level order traversal of a binary tree. We explored the problem definition, approach, algorithm, and provided a detailed C++ implementation. We also analyzed the complexity and discussed edge cases and testing strategies. Understanding and solving such problems is crucial for improving problem-solving skills and preparing for technical interviews.

Additional Resources

For further reading and practice, consider the following resources: