Talentd
FleetCode
Make and follow sheets.Sign in

N-ary Tree Preorder Traversal solution and explanation

EasyDepth-First SearchStackTree5 min read76.7% acceptanceUpdated 9 Apr 2026
LeetCode N-ary Tree Preorder Traversal Solution Explained - JavaNick White · 6:25 · 42K viewsWatch on YouTube
9 more videos on this problem
  1. LeetCode N-ary Tree Preorder Traversal Solution Explained - JavaNick White · 6:25 · 42K views
  2. N-ary Tree Preorder Traversal | Live Coding with Explanation | Leetcode - 589Algorithms Made Easy · 10:04 · 12K views
  3. LeetCode 589 | N-Ary Tree Pre-Order Traversal | Iterative + Recursive Solution (Java + Whiteboard)Xavier Elon · 9:46 · 2K views
  4. N-ary Tree Preorder Traversal | Leetcode 589EasyLeetcode · 4:56 · 1K views
  5. Обхід дерева N ary Tree Preorder Traversal: How to do it fast and easy! Leetcode - 589TechWorld-With-Yevgen · 7:01 · 547 views
  6. 589. N-ary Tree Preorder Traversal | Leetcode | PythonAbdullah Arshed · 5:29 · 516 views
  7. 589. N-ary Tree Preorder Traversal - LeetCode Java SolutionCode with Carter · 7:30 · 424 views
  8. 589. N-ary Tree Preorder Traversal | English | EasySimple Code - 단순코드 · 6:08 · 340 views
  9. N-ary Tree Preorder Traversal - Google Interview Questions - LeetCode 589 Solution - C++nexTRIE · 3:26 · 289 views
  10. Leetcode 589 - N-ary Tree Preorder Traversal (Java, Solution Explained)LC Bear · 4:48 · 230 views

The problem

Given the root of an n-ary tree, return the preorder traversal of its nodes' values.

Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value (See examples)

Example 1:

Input: root = [1,null,3,2,4,null,5,6]
Output: [1,3,5,6,2,4]

Example 2:

Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
Output: [1,2,3,6,7,11,14,4,8,12,5,9,13,10]

Constraints:

  • The number of nodes in the tree is in the range [0, 104].
  • 0 <= Node.val <= 104
  • The height of the n-ary tree is less than or equal to 1000.

Follow up: Recursive solution is trivial, could you do it iteratively?

Approach overview

Problem Overview: Given the root of an N-ary tree, return its preorder traversal. In preorder traversal, you visit the current node first, then recursively process each child from left to right.

Approach 1: Recursive DFS (Time: O(n), Space: O(h))

The most natural way to solve this problem is recursion using Depth-First Search. Start at the root, add the node's value to the result list, then recursively traverse each child in order. Because preorder requires visiting the node before its children, the operation sequence is: visit node → iterate children → recurse. Each node is processed exactly once, giving O(n) time complexity where n is the number of nodes. The recursion stack grows with the height of the tree, so the auxiliary space is O(h). This approach is concise and mirrors the definition of preorder traversal directly.

Approach 2: Iterative Traversal with Stack (Time: O(n), Space: O(n))

An iterative solution replaces recursion with an explicit stack. Push the root node onto the stack, then repeatedly pop the top node, record its value, and push its children onto the stack. The key detail is the order: push children in reverse order so the leftmost child is processed first when popped. This preserves preorder traversal order. Every node enters and leaves the stack once, so the time complexity remains O(n). The stack may contain up to O(n) nodes in the worst case (for example, a wide level in the tree). This method avoids recursion and works well in environments where recursion depth could be limited.

Recommended for interviews: The recursive DFS approach is usually expected first because it directly expresses preorder traversal and is easy to implement correctly. Interviewers often accept it as the primary solution. The iterative stack version demonstrates deeper understanding of traversal mechanics and recursion simulation, which can strengthen your answer if follow-up questions ask for an iterative implementation.

Approach 1: Recursive Approach

This approach leverages the simplicity of recursion to perform a preorder traversal on an n-ary tree. We start at the root node, then recursively process each child's subtree.

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

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        def preorder_helper(node, result):
            if not node:
                return
            result.append(node.val)
            for child in node.children:
                preorder_helper(child, result)

        result = []
        preorder_helper(root, result)
        return result
import java.util.ArrayList;
import java.util.List;

class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};

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

    public void preorderHelper(Node node, List<Integer> result) {
        if (node == null) return;
        result.add(node.val);
        for (Node child : node.children) {
            preorderHelper(child, result);
        }
    }
}
#include <vector>
using namespace std;

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:
    void preorderHelper(Node* node, vector<int>& result) {
        if (!node) return;
        result.push_back(node->val);
        for (Node* child : node->children) {
            preorderHelper(child, result);
        }
    }

    vector<int> preorder(Node* root) {
        vector<int> result;
        preorderHelper(root, result);
        return result;
    }
};
class Node {
    constructor(val, children = []) {
        this.val = val;
        this.children = children;
    }
}

const preorder = function(root) {
    const result = [];
    const preorderHelper = (node) => {
        if (!node) return;
        result.push(node.val);
        for (const child of node.children) {
            preorderHelper(child);
        }
    };
    preorderHelper(root);
    return result;
};
#include <stdio.h>
#include <stdlib.h>

// Definition for a Node.
struct Node {
    int val;
    int numChildren;
    struct Node** children;
};

void preorderHelper(struct Node* node, int* returnArray, int* returnSize) {
    if (node == NULL) return;
    returnArray[(*returnSize)++] = node->val;
    for (int i = 0; i < node->numChildren; i++) {
        preorderHelper(node->children[i], returnArray, returnSize);
    }
}

int* preorder(struct Node* root, int* returnSize) {
    *returnSize = 0;
    int* returnArray = malloc(10000 * sizeof(int)); // Assuming max size
    preorderHelper(root, returnArray, returnSize);
    return returnArray;
}
using System.Collections.Generic;

public class Node {
    public int val;
    public IList<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, IList<Node> _children) {
        val = _val;
        children = _children;
    }
}

public class Solution {
    public IList<int> Preorder(Node root) {
        var result = new List<int>();
        PreorderHelper(root, result);
        return result;
    }

    private void PreorderHelper(Node node, IList<int> result) {
        if (node == null) return;
        result.Add(node.val);
        foreach (var child in node.children) {
            PreorderHelper(child, result);
        }
    }
}

The Python solution uses a nested helper function named preorder_helper that appends node values to a list, result, as it recursively explores each subtree's preorder traversal starting from the root.

Complexity

Time Complexity: O(n), n being the total number of nodes.
Space Complexity: O(n) due to the recursion stack's depth.

Try this approach in the editor

Approach 2: Iterative Approach with Stack

This approach uses an explicit stack to simulate the call stack of recursion. We manually manage traversals seen in recursion, starting from the root node, iteratively processing each node, then adding each child to a stack for future visits.

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

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        if not root:
            return []

        stack, result = [root], []

        while stack:
            node = stack.pop()
            result.append(node.val)
            stack.extend(reversed(node.children))

        return result
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};

class Solution {
    public List<Integer> preorder(Node root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) return result;

        Stack<Node> stack = new Stack<>();
        stack.push(root);

        while (!stack.isEmpty()) {
            Node node = stack.pop();
            result.add(node.val);

            List<Node> children = new ArrayList<>(node.children);
            Collections.reverse(children);
            for (Node child : children) {
                stack.push(child);
            }
        }
        return result;
    }
}
#include <vector>
#include <stack>
using namespace std;

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> preorder(Node* root) {
        vector<int> result;
        if (!root) return result;

        stack<Node*> stk;
        stk.push(root);

        while (!stk.empty()) {
            Node* curr = stk.top();
            stk.pop();
            result.push_back(curr->val);

            for (auto it = curr->children.rbegin(); it != curr->children.rend(); ++it) {
                stk.push(*it);
            }
        }

        return result;
    }
};
class Node {
    constructor(val, children = []) {
        this.val = val;
        this.children = children;
    }
}

const preorder = function(root) {
    const result = [];
    const stack = [];
    if (root) stack.push(root);

    while (stack.length) {
        const node = stack.pop();
        result.push(node.val);

        for (let i = node.children.length - 1; i >= 0; i--) {
            stack.push(node.children[i]);
        }
    }

    return result;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// Definition for a Node.
struct Node {
    int val;
    int numChildren;
    struct Node** children;
};

struct NodeStack {
    struct Node* node;
    struct NodeStack* next;
};

void push(struct NodeStack** stack, struct Node* node) {
    struct NodeStack* new_node = (struct NodeStack*)malloc(sizeof(struct NodeStack));
    new_node->node = node;
    new_node->next = *stack;
    *stack = new_node;
}

struct Node* pop(struct NodeStack** stack) {
    if (*stack == NULL) return NULL;
    struct NodeStack* temp = *stack;
    struct Node* node = temp->node;
    *stack = (*stack)->next;
    free(temp);
    return node;
}

bool isEmpty(struct NodeStack* stack) {
    return stack == NULL;
}

int* preorder(struct Node* root, int* returnSize) {
    *returnSize = 0;
    if (!root) return NULL;

    int* result = malloc(10000 * sizeof(int));
    struct NodeStack* stack = NULL;
    push(&stack, root);

    while (!isEmpty(stack)) {
        struct Node* node = pop(&stack);
        result[(*returnSize)++] = node->val;

        for (int i = node->numChildren - 1; i >= 0; i--) {
            push(&stack, node->children[i]);
        }
    }

    return result;
}
using System.Collections.Generic;

public class Node {
    public int val;
    public IList<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, IList<Node> _children) {
        val = _val;
        children = _children;
    }
}

public class Solution {
    public IList<int> Preorder(Node root) {
        var result = new List<int>();
        if (root == null)
            return result;

        Stack<Node> stack = new Stack<Node>();
        stack.Push(root);

        while (stack.Count > 0) {
            Node currentNode = stack.Pop();
            result.Add(currentNode.val);

            for (int i = currentNode.children.Count - 1; i >= 0; i--) {
                stack.Push(currentNode.children[i]);
            }
        }

        return result;
    }
}

The Python code introduces a list-based manual stack managing the work of the traversal, maintaining the order of the node children such that earlier nodes are faced first due to reversing incorporation into the stack.

Complexity

Time Complexity: O(n), the time through node visits.
Space Complexity: O(n), as characterized by stack in the worst-case storage condition.

Try this approach in the editor

Approach 3: Recursion

We can recursively traverse the entire tree. For each node, we first add the node's value to the answer, then recursively call the function for each of the node's children.

The time complexity is O(n), and the space complexity is O(n). Here, n is the number of nodes.

/**
 * Definition for node.
 * class Node {
 *     val: number
 *     children: Node[]
 *     constructor(val?: number) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.children = []
 *     }
 * }
 */

function preorder(root: Node | null): number[] {
    const ans: number[] = [];
    const dfs = (root: Node | null) => {
        if (!root) {
            return;
        }
        ans.push(root.val);
        for (const child of root.children) {
            dfs(child);
        }
    };
    dfs(root);
    return ans;
}
/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Children []*Node
 * }
 */

func preorder(root *Node) (ans []int) {
	var dfs func(*Node)
	dfs = func(root *Node) {
		if root == nil {
			return
		}
		ans = append(ans, root.Val)
		for _, child := range root.Children {
			dfs(child)
		}
	}
	dfs(root)
	return
}
Try this approach in the editor

Approach 4: Iteration (Stack Implementation)

We can also solve this problem iteratively.

We use a stack to help us get the pre-order traversal. We first push the root node into the stack. Since the pre-order traversal is root, left subtree, right subtree, and the characteristic of the stack is first in last out, we first add the node's value to the answer, then push each of the node's children into the stack in the order from right to left. We continue this process until the stack is empty.

The time complexity is O(n), and the space complexity is O(n). Here, n is the number of nodes.

/**
 * Definition for node.
 * class Node {
 *     val: number
 *     children: Node[]
 *     constructor(val?: number) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.children = []
 *     }
 * }
 */

function preorder(root: Node | null): number[] {
    const ans: number[] = [];
    if (!root) {
        return ans;
    }
    const stk: Node[] = [root];
    while (stk.length) {
        const { val, children } = stk.pop()!;
        ans.push(val);
        for (let i = children.length - 1; i >= 0; i--) {
            stk.push(children[i]);
        }
    }
    return ans;
}
/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Children []*Node
 * }
 */

func preorder(root *Node) (ans []int) {
	if root == nil {
		return
	}
	stk := []*Node{root}
	for len(stk) > 0 {
		node := stk[len(stk)-1]
		ans = append(ans, node.Val)
		stk = stk[:len(stk)-1]
		children := node.Children
		for i := len(children) - 1; i >= 0; i-- {
			stk = append(stk, children[i])
		}
	}
	return
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Recursive Approach

Time Complexity: O(n), n being the total number of nodes.
Space Complexity: O(n) due to the recursion stack's depth.

Iterative Approach with Stack

Time Complexity: O(n), the time through node visits.
Space Complexity: O(n), as characterized by stack in the worst-case storage condition.

Recursion–
Iteration (Stack Implementation)–

Complexity analysis

ApproachTimeSpaceWhen to Use
Recursive DFSO(n)O(h)Best for clarity and typical interview solutions when recursion depth is manageable
Iterative Stack TraversalO(n)O(n)Preferred when avoiding recursion or when practicing explicit stack-based DFS

Frequently asked questions

Is N-ary Tree Preorder Traversal easy or hard?

N-ary Tree Preorder Traversal is classified as an easy problem. It mainly tests understanding of preorder traversal and DFS. Once you know the traversal order (node before children), the recursive implementation is short and direct.

N-ary Tree Preorder Traversal Python/Java solution

Python and Java solutions typically implement recursive DFS with a helper function that visits the node and then iterates through its children list. The iterative approach uses a stack (list in Python or Stack/Deque in Java) to simulate the recursion while maintaining preorder order.

How to solve N-ary Tree Preorder Traversal in O(n)?

Use depth-first search. Start at the root, append its value to the result, and recursively process each child in order. An iterative version uses a stack: pop a node, record its value, and push its children in reverse order so the leftmost child is processed first.

What is the best approach for N-ary Tree Preorder Traversal?

Recursive depth-first search is the most straightforward approach. Visit the current node, then recursively traverse each child from left to right. It runs in O(n) time because every node is visited once, and uses O(h) space for the recursion stack where h is the tree height.

Is N-ary Tree Preorder Traversal asked at Google/Amazon/Meta?

Tree traversal problems appear frequently in interviews at companies like Google, Amazon, and Meta. Variants involving binary trees, N-ary trees, and DFS traversal patterns are commonly used to test understanding of recursion, stacks, and tree data structures.

What data structure is used in N-ary Tree Preorder Traversal?

The traversal uses either the call stack through recursion or an explicit stack data structure for the iterative solution. The tree itself stores a list of children for each node, which you iterate through during the DFS process.

What is the time complexity of N-ary Tree Preorder Traversal?

The time complexity is O(n), where n is the number of nodes in the tree. Each node is visited exactly once during the traversal. Both recursive DFS and iterative stack implementations maintain the same time complexity.

Comments

Now write it yourselfClose this page and solve N-ary Tree Preorder Traversal in the editor, without looking.
Practise N-ary Tree Preorder Traversal