
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
1
has no left child and a right child2
. - The node
2
has 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:
1
is the root, with left child2
and right child3
.2
has children4
(left) and5
(right).5
has further children6
(left) and7
(right).3
has right child8
, and8
has further left child9
.
- The preorder traversal sequence:
1
, then left child2
, continue to deepest left4
, backtrack and go to5
, then to its children6
and7
, backtrack to3
and 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>
namedresult
to store the values of the nodes as they are visited. - Start with the
root
node and use a pointercurrent
to traverse the tree. - Enter a while loop which continues as long as
current
is notnullptr
. - Inside the loop, check if the left child of
current
isnullptr
:- If true, record the value of
current
intoresult
. - Move
current
to 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 tocurrent
to avoid usage of stack or recursion. - Check if the temporary link (
temp->right
) is already created:- If
temp->right
isnullptr
, maketemp->right
point tocurrent
(creating a temporary link), save thecurrent
node's value in the result vector, and movecurrent
to its left child. - If
temp->right
points tocurrent
, this means you're revisiting the node and should remove the temporary link and movecurrent
to its right child.
- If
- The loop ends when all nodes are visited, and
current
becomesnullptr
. - Return the
result
vector 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>
namedresult
that will store the values of visited nodes.Initialize a
TreeNode
pointer, namedcurrent
, to the root of the tree.Use a
while
loop to traverse the tree, continuing untilcurrent
isnull
.Inside the loop, check if the
left
child ofcurrent
isnull
:- If
true
, add the value ofcurrent
to theresult
list and move to itsright
child. - If the
left
child is notnull
, locate the rightmost node of the left subtree using a nestedwhile
loop:- Keep traversing to the
right
child of this subtree until a node is found that either has noright
child or theright
child iscurrent
.
- Keep traversing to the
- After exiting the nested loop, there are two scenarios to address:
- If the rightmost node's
right
pointer isnull
:- Add
current
's value to the result. - Set that node's
right
pointer tocurrent
to mark it as visited, then movecurrent
to its left child.
- Add
- If the rightmost node's
right
pointer links back tocurrent
:- Set the
right
pointer of this node tonull
to restore the tree's structure. - Move to the
right
child ofcurrent
.
- Set the
- If the rightmost node's
- If
Finally, return the
result
list 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
result
is 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
size
represents the count of elements currently in theresult
array. Initialize*size
to 0. - A loop navigates the tree. If the current node's left child is absent, the node value is added to the
result
array 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->right
equalsNULL
), 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
preorderTraversal
that acceptsrootNode
which represents the root of the tree. - Initialize an array
results
to store the values of the nodes as you traverse the tree. - Use a
while
loop 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
results
and 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
results
array 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
current
pointer to theroot_node
andresult
, an empty list that will store the values of the visited nodes. - Use a
while
loop to traverse through the tree as long ascurrent
is notNone
. - Inside the loop, if the current node does not have a left child, append its value to
result
and move to the right child. - If a left child exists, find the rightmost node (referred as
pre
) of the left subtree. - If
pre.right
isNone
, it means this subtree hasn't been fully traversed. Thus, setpre.right
tocurrent
(creating a temporary thread back to the current node), record the value ofcurrent
, and movecurrent
to its left child. - If
pre.right
points back to the current node, it means the left subtree has been traversed. Therefore, remove the temporary thread by settingpre.right
toNone
, and movecurrent
to its right child. - Continue this process until all nodes are visited and the list
result
contains 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.
No comments yet.