Given the root
of a binary tree, return the inorder traversal of its nodes' values.
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:
[0, 100]
.-100 <= Node.val <= 100
Follow up: Recursive solution is trivial, could you do it iteratively?
This problem asks for an inorder traversal of a binary tree. Inorder traversal visits nodes in the order: left subtree, root, right subtree. We'll explore three solutions: recursive, iterative using a stack, and iterative using Morris traversal.
This is the most straightforward approach, leveraging the inherent recursive nature of trees.
Algorithm:
Code (Python):
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
Time Complexity: O(N), where N is the number of nodes in the tree. Each node is visited exactly once.
Space Complexity: O(H), where H is the height of the tree. This is due to the recursive call stack. In the worst case (a skewed tree), H could be N. In the best case (a balanced tree), H is log₂N.
This approach avoids recursion by using a stack to simulate the recursive calls.
Algorithm:
stack
and an empty result list result
.result
, and move to its right child.Code (Python):
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
result = []
stack = []
current = root
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append(current.val)
current = current.right
return result
Time Complexity: O(N) - Each node is visited and processed once.
Space Complexity: O(H) - In the worst case (a skewed tree), the stack can grow to the height of the tree (H). In a balanced tree, H = log₂N.
This approach is space-efficient, achieving O(1) space complexity. It cleverly uses the tree structure itself to track the traversal.
Algorithm:
result
.result
and move to the right child.result
, set the predecessor's right child to null (to disconnect the temporary link), and move to the right child.Code (Python):
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
result = []
current = root
while current:
if not current.left:
result.append(current.val)
current = current.right
else:
predecessor = current.left
while predecessor.right and predecessor.right != current:
predecessor = predecessor.right
if not predecessor.right:
predecessor.right = current
current = current.left
else:
result.append(current.val)
predecessor.right = None
current = current.right
return result
Time Complexity: O(N) - Each node is visited and processed once.
Space Complexity: O(1) - No extra space is used beyond a few pointers.
Summary Table:
| Approach | Time Complexity | Space Complexity | |-----------------|-----------------|--------------------| | Recursive | O(N) | O(H) | | Iterative (Stack)| O(N) | O(H) | | Morris Traversal| O(N) | O(1) |
The choice of which solution to use depends on the specific constraints of your problem. If space is a critical concern, Morris traversal is the best option. Otherwise, the recursive or iterative stack-based approaches are generally simpler to understand and implement.