
Problem Statement
In the context of binary trees, an inorder traversal is a technique to visit all the nodes in a specific sequence. Specifically, for any given node, inorder traversal processes nodes in this order: left child, current node, right child. This method ensures that nodes are visited in their non-decreasing order if the binary tree is a Binary Search Tree (BST). The challenge described here involves implementing a function that performs an inorder traversal on a binary tree, given the root of the tree, and returns the values of the nodes as they are visited.
Examples
Example 1
Input:
root = [1,null,2,3]
Output:
[1,3,2]
Explanation:
Example 2
Input:
root = [1,2,3,4,5,null,8,null,null,6,7,9]
Output:
[4,2,6,5,7,1,3,9,8]
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
Inorder traversal of a binary tree can be approached in several ways, commonly through recursive and iterative methods. Both methods ensure that each node is visited in the correct sequence of left child -> node -> right child. Understanding the structure of the examples and constraints provided helps to clarify the approach:
Example 1:
- Given a simple tree where only the root and one of the children have further child.
- Expected to return nodes in the typical left-node-right order even when parts of the tree are missing (null values).
Example 2:
- A more complex tree involving multiple layers.
- Successive children are nested within left and right children indicating deeper traversal before moving onto adjacent higher-level nodes.
Example 3:
- An empty tree where no nodes are present.
- The function should handle cases where the traversal does not start, thus returning an empty list.
Example 4:
- The smallest non-empty structure, a single node with no children, serves as a base case for the recursive or iterative approach.
Given these examples and the constraints:
- The number of trees nodes can be zero (empty tree) to a hundred, thus complexity should be efficient but the method must handle varied depths and structures efficiently.
- Values within the tree can range between "-100 to 100," but this has no direct impact on the traversal method, only possibly on filtering or conditional traversals which are not required in this task.
With these considerations, one may outline two common methods:
Recursive Approach:
- If the current node is null, return (base case).
- Recur on the left child.
- Visit the current node (add the node's value to the result list).
- Recur on the right child.
Iterative Approach with Stack:
- Use a stack to manage the nodes to be visited.
- Push left children of current or popped node until reach null.
- When left is null, pop from the stack, process the node, and consider right children.
- Repeat until all nodes are processed.
Both methods can be implemented simply, yet provide robust means to traverse all types of binary trees.
Solutions
- C++
- Java
- JavaScript
- Python
class Solution {
public:
vector<int> inorderTreeTraversal(TreeNode* root) {
vector<int> output;
TreeNode* current = root;
TreeNode* precursor;
while (current != nullptr) {
if (current->left == nullptr) {
output.push_back(current->val);
current = current->right;
} else {
precursor = current->left;
while (precursor->right != nullptr && precursor->right != current) {
precursor = precursor->right;
}
if (precursor->right == nullptr) {
precursor->right = current;
current = current->left;
} else {
precursor->right = nullptr;
output.push_back(current->val);
current = current->right;
}
}
}
return output;
}
};
The provided C++ code demonstrates an approach to the binary tree inorder traversal using a well-known algorithm known as Morris Traversal. This algorithm effectively traverses a binary tree without using recursion or additional memory for a stack, making it space-efficient with O(1) extra space complexity.
Here’s a breakdown of the approach:
- Initialize a vector,
output
, to store the value of nodes in inorder sequence. - Use a
current
pointer to traverse the tree, starting from the root. - Use another pointer called
precursor
to find the inorder predecessor of the current node.
The Steps in the Morris Traversal Algorithm
- While the
current
node is not NULL:- Check if the
current
node's left child is NULL.- If true, add the
current
’s value to the output. Move to the right child of thecurrent
.
- If true, add the
- If the left child is not NULL:
- Set
precursor
to the left child ofcurrent
. - Move
precursor
to its rightmost child that is not linked back to thecurrent
node (These links are temporarily made in this algorithm to remember the path without using extra memory). - If the right child of
precursor
is NULL:- Make its right child point back to
current
(temporary threading of the tree), and movecurrent
to its left child.
- Make its right child point back to
- If the right child of
precursor
points tocurrent
:- Remove the temporary link (set
precursor
’s right child to NULL), addcurrent
's value to output, and movecurrent
to its right child.
- Remove the temporary link (set
- Set
- Check if the
This traversal ensures that each node's left subtree is visited before the node itself and finally its right subtree, adhering to the inorder traversal requirement. The beauty of this solution lies in its efficiency and lower memory requirement, making it optimal for situations where memory usage is a critical concern.
class Solution {
public List<Integer> inorder(TreeNode root) {
List<Integer> output = new ArrayList<>();
TreeNode current = root;
TreeNode predecessor;
while (current != null) {
if (current.left == null) {
output.add(current.val);
current = current.right;
} else {
predecessor = current.left;
while (predecessor.right != null && predecessor.right != current) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
predecessor.right = current;
current = current.left;
} else {
predecessor.right = null;
output.add(current.val);
current = current.right;
}
}
}
return output;
}
}
The solution implements an iterative method for Inorder Traversal of a Binary Tree using Java. Here's a succinct breakdown of the logic employed in the provided code:
- Begin with the root of the binary tree.
- Use a while loop to process nodes until there are no more nodes to process (
current != null
). - Inside the loop, check if the left child of the current node is
null
:- If
true
, add the current node's value to theoutput
list and proceed to the right child.
- If
- If the left child is not
null
, find the rightmost node of the left subtree, termed aspredecessor
.- Traverse to this
predecessor
node using a nested while loop until its right child isnull
or the current node itself. - Then:
- If
predecessor.right
isnull
, establish a temporary thread (Morris threading) to the current node and move to the left child of the current. - If
predecessor.right
points to the current node, it indicates a return to a threaded node, hereby, remove the temporary thread and:- Add the current node's value to
output
. - Move to the right child of the current node.
- Add the current node's value to
- If
- Traverse to this
- The traversal continues until all nodes are processed, and the function returns the
output
list containing the values in Inorder sequence.
This approach reduces the space complexity by avoiding the use of a stack, typically used in recursive and iterative deep tree traversals, by utilizing a threading technique known as Morris Traversal to link the tree nodes temporarily.
function TreeNode(value, leftChild, rightChild) {
this.value = (value === undefined ? 0 : value);
this.left = (leftChild === undefined ? null : leftChild);
this.right = (rightChild === undefined ? null : rightChild);
}
function inorder(root) {
let result = [];
let current = root;
let predecessor;
while (current != null) {
if (current.left == null) {
result.push(current.value);
current = current.right; // Move right
} else {
predecessor = current.left;
while (predecessor.right !== null && predecessor.right !== current) {
predecessor = predecessor.right;
}
if (predecessor.right === null) {
// Make a temporary link to current
predecessor.right = current;
current = current.left;
} else {
// Revert the changes made in the tree
predecessor.right = null;
result.push(current.value);
current = current.right;
}
}
}
return result;
}
To perform an inorder traversal on a binary tree using JavaScript, you utilize the Morris Traversal method. This approach eliminates the need for an auxiliary stack or recursion, making it memory efficient, particularly useful for large trees.
Solution Breakdown:
TreeNode Structure:
- Define a
TreeNode
function to create nodes of the binary tree. Each node might include a value and pointers to its left and right children.
- Define a
Inorder Traversal Function:
- Define the
inorder
function that accepts the root of the tree as its parameter and returns an array of values representing the inorder traversal. - Initialize an empty array
result
to store the traversal result. - Use a
current
pointer to navigate through the tree, starting at the root.
- Define the
Traversal Process:
- Traverse the tree using a
while
loop until thecurrent
pointer isnull
. - If the current node (
current
) has no left child, append its value toresult
and move to the right child. - If the current node has a left child, find the inorder predecessor of the current node in its left subtree (the rightmost node in the left subtree).
- If the predecessor's right pointer is
null
, set it temporarily to the current node, facilitating a return after visiting the left subtree, then move the current to its left child. - If the predecessor's right pointer points to the current node, it indicates that the left subtree has been fully visited. At this point, break this temporary link to restore the tree's original structure, append the current node's value to
result
, and move to the right child.
- Traverse the tree using a
Output:
- After exiting the loop, the
result
array contains the values of the nodes in inorder sequence.
- After exiting the loop, the
The Morris Traversal method offers an efficient way to traverse a tree without additional space for recursion stack or other data structures. This makes it particularly suitable for scenarios where space efficiency is a priority.
function TreeNode(value, leftChild, rightChild) {
this.value = (value === undefined ? 0 : value);
this.left = (leftChild === undefined ? null : leftChild);
this.right = (rightChild === undefined ? null : rightChild);
}
function inorder(root) {
let result = [];
let current = root;
let predecessor;
while (current != null) {
if (current.left == null) {
result.push(current.value);
current = current.right; // Move right
} else {
predecessor = current.left;
while (predecessor.right !== null && predecessor.right !== current) {
predecessor = predecessor.right;
}
if (predecessor.right === null) {
// Make a temporary link to current
predecessor.right = current;
current = current.left;
} else {
// Revert the changes made in the tree
predecessor.right = null;
result.push(current.value);
current = current.right;
}
}
}
return result;
}
class Solution:
def inorder(self, node: TreeNode) -> List[int]:
output = []
current = node
while current is not None:
if current.left is None:
output.append(current.val)
current = current.right
else:
predecessor = current.left
while predecessor.right and predecessor.right != current:
predecessor = predecessor.right
if predecessor.right is None:
predecessor.right = current
current = current.left
else:
predecessor.right = None
output.append(current.val)
current = current.right
return output
The given Python solution details a non-recursive approach for performing an inorder traversal of a binary tree using Morris traversal technique. Implement this method to ensure effective traversal without the use of additional memory (like recursion stack or an explicit stack data structure). Follow these steps to understand the operations:
Initialize with the root node.
Use a loop to traverse through the nodes while the current node is not
None
.Inside the loop, check if the
current.left
isNone
. If it is, append thecurrent.val
to the output and move to thecurrent.right
.If
current.left
is notNone
, initiate another traversal to find the rightmost node of the left subtree (predecessor
).Ensure in this inner loop that you stop when
predecessor.right
is eitherNone
or equal tocurrent
.Update the right pointer of the predecessor:
- If
predecessor.right
isNone
, direct it towardscurrent
, and shift thecurrent
pointer tocurrent.left
. - If
predecessor.right
points tocurrent
, reset it toNone
, appendcurrent.val
to output, then move tocurrent.right
.
- If
When the outer loop completes, the
output
list contains the inorder traversal of the tree.
Remember, this Morris Traversal method modifies the tree by temporarily adding additional pointers but ensures to restore the tree back to its original structure as the algorithm proceeds. Adhere to this technique for an optimized traversal of a binary tree, especially in environments where memory efficiency is critical.
No comments yet.