Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

590. N-ary Tree Postorder Traversal - Leetcode Solution

Code Implementation

# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children if children is not None else []

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        result = []
        def dfs(node):
            if not node:
                return
            for child in node.children:
                dfs(child)
            result.append(node.val)
        dfs(root)
        return result
      
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};

class Solution {
public:
    vector<int> postorder(Node* root) {
        vector<int> result;
        dfs(root, result);
        return result;
    }
    void dfs(Node* node, vector<int>& result) {
        if (!node) return;
        for (Node* child : node->children) {
            dfs(child, result);
        }
        result.push_back(node->val);
    }
};
      
import java.util.*;

// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int val) {
        this.val = val;
    }

    public Node(int val, List<Node> children) {
        this.val = val;
        this.children = children;
    }
}

class Solution {
    public List<Integer> postorder(Node root) {
        List<Integer> result = new ArrayList<>();
        dfs(root, result);
        return result;
    }

    private void dfs(Node node, List<Integer> result) {
        if (node == null) return;
        for (Node child : node.children) {
            dfs(child, result);
        }
        result.add(node.val);
    }
}
      
/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children || [];
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 */
var postorder = function(root) {
    const result = [];
    function dfs(node) {
        if (!node) return;
        for (const child of node.children) {
            dfs(child);
        }
        result.push(node.val);
    }
    dfs(root);
    return result;
};
      

Problem Description

Given the root of an N-ary tree, return the postorder traversal of its nodes' values. An N-ary tree is a tree in which each node can have zero or more children. In postorder traversal, you recursively visit all the children from left to right, and then visit the node itself.

  • You are given a root node representing the root of the N-ary tree.
  • Return a list of integers representing the postorder traversal of the tree.
  • If the tree is empty (root is null), return an empty list.

Thought Process

To solve this problem, we need to understand how postorder traversal works for an N-ary tree. In a binary tree, postorder means left child, right child, then node. For an N-ary tree, we generalize this to: visit all children in order, then the node itself.

The brute-force idea is to use recursion: for each node, recursively traverse all its children, and after that, add the node's value to the result. This is a natural fit for recursion because the tree's structure is inherently recursive.

If we wanted to optimize or do this iteratively, we could use a stack to simulate the call stack, but recursion is simple and effective for this problem unless the tree is very deep.

Solution Approach

Let's outline the step-by-step solution for postorder traversal of an N-ary tree:

  1. Base Case: If the current node is null, return immediately. This handles empty trees.
  2. Recursive Traversal: For each child in the current node's children list, recursively perform postorder traversal.
  3. Visit Node: After traversing all the children, add the current node's value to the result list.
  4. Return Result: After the traversal is complete, return the result list.

We use recursion because it matches the tree's structure and keeps the code clean. In each recursive call, we process all the children first (from left to right), and then the node itself, which is exactly what postorder requires.

If you want to avoid recursion (for very deep trees), you can use an explicit stack, but recursion is preferred for clarity and simplicity in most interview scenarios.

Example Walkthrough

Let's walk through an example. Suppose the N-ary tree is:

            1
         /  |  \
        3   2   4
       / \
      5   6
  

The postorder traversal should be: [5, 6, 3, 2, 4, 1]

  1. Start at node 1. Its children are 3, 2, and 4.
  2. Visit child 3:
    • Child 3 has children 5 and 6.
    • Visit child 5: no children, so add 5 to result.
    • Visit child 6: no children, so add 6 to result.
    • After all children, add 3 to result.
  3. Visit child 2: no children, so add 2 to result.
  4. Visit child 4: no children, so add 4 to result.
  5. After all children of 1, add 1 to result.

The result builds up as follows: [5, 6, 3, 2, 4, 1]

Time and Space Complexity

  • Brute-force approach: Visiting every node and every child, so the time complexity is O(N), where N is the total number of nodes.
  • Space complexity (recursive): The extra space comes from the recursion stack. In the worst case (a skewed tree), the stack could be O(N) deep. The result list also takes O(N) space.
  • Iterative approach: Also O(N) time and O(N) space due to the explicit stack and result list.

In both cases, the algorithm is efficient and optimal for this problem.

Summary

The N-ary Tree Postorder Traversal problem is a classic example of recursive tree processing. By traversing all children before visiting the node itself, we achieve postorder traversal. The recursive approach is natural and efficient, with both time and space complexity proportional to the number of nodes in the tree. This problem reinforces the value of recursion for hierarchical data structures and highlights the simplicity and elegance of postorder traversal in trees of arbitrary branching factor.