LC94. Binary Tree Inorder Traversal¶
Problem Description¶
LeetCode Problem 94: Given the root of a binary tree, return the inorder traversal of its nodes' values.
Clarification¶
- Definition of inorder traversal
Assumption¶
-
Solution¶
Approach 1 - Recursion¶
This problem can be solved using the classic recursive method by defining a new helper function
inorder
. In the helper function,
inorder(root.left, values) # recursively traverse the left subtree
values.append(root.val) # visit root/parent node
inorder(root.right, values) # recursively traverse the right subtree
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
result = []
self.inorder(root, result)
return result
def inorder(self, node: Optional[TreeNode], result: List[int]):
if node is None:
return
self.inorder(node.left, result)
result.append(node.val)
self.inorder(node.right, result)
Complexity Analysis of Approach 1¶
- Time complexity: \(O(n)\)
The algorithm visits each node exactly once for total \(n\) nodes. - Space complexity: \(O(n)\)
The recursion stack takes \(O(n)\) space in the worst case (for example, every node only has left child). In the best case (a balanced binary tree), the recursion stack takes \(O(\log n)\) space.
Approach 2 - Iteration with Stack¶
Instead of using recursion, we can traverse the nodes by using stack to store the nodes and pop it up when reaching the end of the left branch.
from collections import deque
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
values = []
stack = deque()
curr_node = root
while curr_node or stack:
if curr_node:
stack.append(curr_node) # (1)
curr_node = curr_node.left
else:
curr_node = stack.pop()
values.append(curr_node.val) # (2)
curr_node = curr_node.right
return values
- To return the root later.
- Append after all left children.
Complexity Analysis of Approach 2¶
- Time complexity: \(O(n)\)
Each node (total \(n\) nodes) is pushed once to and popped once from the stack. So the time complexity is \(O(2n) = O(n)\). - Space complexity: \(O(n)\)
- In the worst case (for example, every node only has left child), the stack will hold all \(n\) nodes.
- In the best case (a balanced binary tree), stack will hold \(\log n\) nodes due to the height of the tree.
Approach 3 - Morris Traversal¶
We can use Morris traversal which is similar to the solution in preorder traversal. The only difference is in when to visit node after finding predecessor:
- Preorder traversal: visit node when
predecessor.right
isNone
(before exploring left), since root/parent needs to be the first (root - left - right) before exploring the left. - Inorder traversal: visit node when
predecessor.right
iscurr_node
(after exploring left), since root/parent needs to be middle (left - middle - right).
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
output = []
curr_node = root
while curr_node:
if curr_node.left:
# Find predecessor, the rightmost node of the left sub-tree.
predecessor = curr_node.left
while predecessor.right and predecessor.right != curr_node:
predecessor = predecessor.right
# Handle two conditions out of while loops
if not predecessor.right: # (1)
predecessor.right = curr_node
curr_node = curr_node.left
else: # (2)
output.append(curr_node.val)
predecessor.right = None
curr_node = curr_node.right
else:
output.append(curr_node.val)
curr_node = curr_node.right
return output
- Condition 1:
predecessor.right
isNone
. The left sub-tree is not explored. - Condition 2:
predecessor.right
is thecurr_node
. The left sub-tree has been visited.
Complexity Analysis of Approach 3¶
- Time complexity: \(O(n)\)
We visit each node twice, one for establishing link and one for removing. Therefore the time complexity is \(O(n)\). - Space complexity: \(O(n)\)
- Visit nodes taking \(O(1)\) space.
- The return list takes \(O(n)\) space to save all node values.
- The total space complexity is \(O(n) = O(1) + O(n)\).
Comparison of Different Approaches¶
The table below summarize the time complexity and space complexity of different approaches:
Approach | Time Complexity | Space Complexity (Overall) | Space Complexity (Visit) |
---|---|---|---|
Approach - Recursion | \(O(n)\) | \(O(n)\) | $O(n) |
Approach - Iteration | \(O(n)\) | \(O(n)\) | $O(n) |
Approach - Morris | \(O(n)\) | \(O(n)\) | $O(1) |