
Problem Statement
In the given task, you are asked to perform a preorder traversal for a binary tree starting from the node referred to as root. During a preorder traversal, the sequence of operations is: Visiting the root node first, then recursively performing a preorder traversal on the left subtree, followed by the right subtree. The aim is to record and return the values of the nodes in the order they are visited. This form of traversal is particularly useful in scenarios like expression tree evaluations and syntax tree traversal, where the root precedence is critical.
Examples
Example 1
Input:
root = [1,null,2,3]
Output:
[1,2,3]
Explanation:
Example 2
Input:
root = [1,2,3,4,5,null,8,null,null,6,7,9]
Output:
[1,2,4,5,6,7,3,8,9]
Explanation:
Example 3
Input:
root = []
Output:
[]
Example 4
Input:
root = [1]
Output:
[1]
Constraints
- The number of nodes in the tree is in the range
[0, 100]. -100 <= Node.val <= 100
Approach and Intuition
A binary tree preorder traversal involves visiting the nodes in a Root-Left-Right sequence. Here's an intuitive breakdown and a step-by-step explanation for the examples provided:
Example 1:
- Input:
root = [1, null, 2, 3] - The binary tree is:
- The root
1has no left child and a right child2. - The node
2has left child3.
- The root
- Preorder traversal visits the root
1, moves to the right child2, and then to the left child3(Root-Right-Left, because no left child for1). - Output:
[1, 2, 3]
- Input:
Example 2:
- Input:
root = [1, 2, 3, 4, 5, null, 8, null, null, 6, 7, 9] - The tree structure:
1is the root, with left child2and right child3.2has children4(left) and5(right).5has further children6(left) and7(right).3has right child8, and8has further left child9.
- The preorder traversal sequence:
1, then left child2, continue to deepest left4, backtrack and go to5, then to its children6and7, backtrack to3and its subtree8, then9. - Output:
[1, 2, 4, 5, 6, 7, 3, 8, 9]
- Input:
Example 3:
- Input:
root = [] - An empty tree returns an empty list.
- Output:
[]
- Input:
Example 4:
- Input:
root = [1] - The tree has only one node
1. - Output:
[1]
- Input:
These examples illustrate various scenarios from an empty tree to a more densely populated one, showing the practical application of the Root-Left-Right traversal principle inherent in preorder traversals. The consistency observed in this traversal method lends straightforwardness to tasks such as tree serialization and making copies of the tree.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class Solution {
public:
vector<int> traversePreorder(TreeNode* root) {
vector<int> result;
TreeNode* current = root;
while (current != nullptr) {
if (current->left == nullptr) {
result.push_back(current->val);
current = current->right;
} else {
TreeNode* temp = current->left;
while (temp->right != nullptr && temp->right != current) {
temp = temp->right;
}
if (temp->right == nullptr) {
result.push_back(current->val);
temp->right = current;
current = current->left;
} else {
temp->right = nullptr;
current = current->right;
}
}
}
return result;
}
};
The provided C++ code implements the Binary Tree Preorder Traversal using an iterative approach with the Morris Traversal technique. Here's how the function traversePreorder operates:
- Initialize a
vector<int>namedresultto store the values of the nodes as they are visited. - Start with the
rootnode and use a pointercurrentto traverse the tree. - Enter a while loop which continues as long as
currentis notnullptr. - Inside the loop, check if the left child of
currentisnullptr:- If true, record the value of
currentintoresult. - Move
currentto its right child.
- If true, record the value of
- If the left child is not
nullptr, find the rightmost node of the left subtree; this node will temporarily link back tocurrentto avoid usage of stack or recursion. - Check if the temporary link (
temp->right) is already created:- If
temp->rightisnullptr, maketemp->rightpoint tocurrent(creating a temporary link), save thecurrentnode's value in the result vector, and movecurrentto its left child. - If
temp->rightpoints tocurrent, this means you're revisiting the node and should remove the temporary link and movecurrentto its right child.
- If
- The loop ends when all nodes are visited, and
currentbecomesnullptr. - Return the
resultvector containing the preorder traversal of the tree.
This approach is memory efficient as it does not use additional data structures like stack or recursion for maintaining tree traversal state, and it modifies the tree temporarily during traversal to achieve this.
class Solution {
public List<Integer> iterativePreorder(TreeNode root) {
LinkedList<Integer> result = new LinkedList<>();
TreeNode current = root;
while (current != null) {
if (current.left == null) {
result.add(current.val);
current = current.right;
} else {
TreeNode temp = current.left;
while (
(temp.right != null) && (temp.right != current)
) {
temp = temp.right;
}
if (temp.right == null) {
result.add(current.val);
temp.right = current;
current = current.left;
} else {
temp.right = null;
current = current.right;
}
}
}
return result;
}
}
The following Java solution outlines an approach to perform a Preorder Traversal on a binary tree using an iterative method:
Create a
LinkedList<Integer>namedresultthat will store the values of visited nodes.Initialize a
TreeNodepointer, namedcurrent, to the root of the tree.Use a
whileloop to traverse the tree, continuing untilcurrentisnull.Inside the loop, check if the
leftchild ofcurrentisnull:- If
true, add the value ofcurrentto theresultlist and move to itsrightchild. - If the
leftchild is notnull, locate the rightmost node of the left subtree using a nestedwhileloop:- Keep traversing to the
rightchild of this subtree until a node is found that either has norightchild or therightchild iscurrent.
- Keep traversing to the
- After exiting the nested loop, there are two scenarios to address:
- If the rightmost node's
rightpointer isnull:- Add
current's value to the result. - Set that node's
rightpointer tocurrentto mark it as visited, then movecurrentto its left child.
- Add
- If the rightmost node's
rightpointer links back tocurrent:- Set the
rightpointer of this node tonullto restore the tree's structure. - Move to the
rightchild ofcurrent.
- Set the
- If the rightmost node's
- If
Finally, return the
resultlist which now contains the Preorder traversal of the binary tree.
This method leverages the Morris traversal technique, which reduces the space complexity by avoiding the use of a stack, typically employed in recursive and other iterative approaches. This technique manipulates the tree's pointers to mark visited nodes, thereby eliminating the need for additional memory for traversal state.
int* preorder(struct TreeNode* root, int* size) {
int* result = (int*)malloc(100 * sizeof(int));
*size = 0;
struct TreeNode* current = root;
while (current != NULL) {
if (current->left == NULL) {
result[(*size)++] = current->val;
current = current->right;
} else {
struct TreeNode* temp = current->left;
while (temp->right != NULL && temp->right != current) {
temp = temp->right;
}
if (temp->right == NULL) {
result[(*size)++] = current->val;
temp->right = current;
current = current->left;
} else {
temp->right = NULL;
current = current->right;
}
}
}
return result;
}
The provided C code defines a function preorder to perform a preorder traversal on a binary tree without using recursion. Instead, it uses a specific technique known as Morris Traversal, which manipulates the tree nodes to avoid using an auxiliary stack or recursion.
- The function signature,
int* preorder(struct TreeNode* root, int* size), indicates it accepts a binary tree root and a pointer to track the size of the resulting array. - An integer pointer
resultis allocated memory enough for 100 integers, expecting the binary tree could have up to 100 nodes. Adjust the allocated memory based on specific needs or implement dynamic resizing for better adaptability. - The variable
sizerepresents the count of elements currently in theresultarray. Initialize*sizeto 0. - A loop navigates the tree. If the current node's left child is absent, the node value is added to the
resultarray and traversal moves to the right child. - If the left child exists, the code finds the rightmost node in the left subtree that doesn't point back to the current node to avoid cycles.
- If this rightmost node has no threaded pointer to the current node (
temp->rightequalsNULL), the current node's value is added to the result array and a temporary threaded link is created between this rightmost node and the current node. The traversal then moves left. - If a threaded link exists indicating this subtree has been visited, it gets removed, and traversal shifts right.
This method provides an efficient way to traverse a binary tree without additional space overhead, such as recursion call stacks or extra data structures, by using the Morris Traversal technique.
var preorderTraversal = function (rootNode) {
let results = [];
let current = rootNode;
while (current) {
if (!current.left) {
results.push(current.val);
current = current.right;
} else {
let lastLeft = current.left;
while (lastLeft.right && lastLeft.right !== current) {
lastLeft = lastLeft.right;
}
if (!lastLeft.right) {
results.push(current.val);
lastLeft.right = current;
current = current.left;
} else {
lastLeft.right = null;
current = current.right;
}
}
}
return results;
};
This guide provides an overview of implementing a binary tree preorder traversal using JavaScript. The task involves using a non-recursive technique with the Morris Traversal method, optimizing the solution by not utilizing additional memory for recursion call stacks or stack data structures.
- Define a function
preorderTraversalthat acceptsrootNodewhich represents the root of the tree. - Initialize an array
resultsto store the values of the nodes as you traverse the tree. - Use a
whileloop to navigate through the nodes of the binary tree starting from therootNode.- If the current node does not have a left child, push the value of the current node to
resultsand move to the right child. - If the current node has a left child, find the rightmost node of the left subtree (but not return to the current node).
- If the rightmost node does not link back to the current node, record the current node’s value, set its right pointer to the current node, then move to the left child of the current node.
- If the rightmost node of the left subtree already links back to the current node, remove that link and move to its right child.
- If the current node does not have a left child, push the value of the current node to
- Repeat this process until no nodes are left to visit.
- Return the
resultsarray containing the preorder traversal of the binary tree.
This method of binary tree traversal achieves the goal without using additional space for recursive stack frames or a separate stack, which makes it space-efficient, especially for large trees. This approach is particularly useful in scenarios where memory usage is a constraint.
class Solution:
def preorder(self, root_node: TreeNode) -> List[int]:
current, result = root_node, []
while current:
if not current.left:
result.append(current.val)
current = current.right
else:
pre = current.left
while pre.right and pre.right is not current:
pre = pre.right
if not pre.right:
result.append(current.val)
pre.right = current
current = current.left
else:
pre.right = None
current = current.right
return result
This Python3-based code snippet executes a Preorder Traversal on a binary tree. Preorder traversal, a common tree traversal technique, involves processing the root node before its child nodes. The code effectively leverages an iterative approach, utilizing Morris Traversal strategy, which optimizes memory usage by avoiding the use of a stack and modifying the tree structure during the traversal.
Understand the flow of the code:
- Initialize a
currentpointer to theroot_nodeandresult, an empty list that will store the values of the visited nodes. - Use a
whileloop to traverse through the tree as long ascurrentis notNone. - Inside the loop, if the current node does not have a left child, append its value to
resultand move to the right child. - If a left child exists, find the rightmost node (referred as
pre) of the left subtree. - If
pre.rightisNone, it means this subtree hasn't been fully traversed. Thus, setpre.righttocurrent(creating a temporary thread back to the current node), record the value ofcurrent, and movecurrentto its left child. - If
pre.rightpoints back to the current node, it means the left subtree has been traversed. Therefore, remove the temporary thread by settingpre.righttoNone, and movecurrentto its right child. - Continue this process until all nodes are visited and the list
resultcontains the values of the nodes in preorder sequence.
By the end of the traversal, the result list will contain the values of the nodes in the order they were visited. This technique is space efficient as it only requires space proportional to the depth of the tree, making it particularly useful for large trees.