Morris Traversal can traverse binary tree without recursion and stack, i.e. O(1)
space usage.
In this article, we discuss the inorder Morris Traversal.
Introduction
Hint: How to go back to the root after processing the left subtree? A: we need a temp link from the root’s predecessor to the root, and we need a mechanism to remove it.
Hint: How to find a predecessor of a node in a binary tree? A:
/* Find the inorder predecessor of current */
pre = current.left;
while (pre.right != null)
pre = pre.right;
Algorithm
pseudo-code
current = root
while current is not null
if not exists current.left
visit(current)
current = current.right # simply go to the right subtree
else
predecessor = findPredecessor(current)
if not exists predecessor.right
predecessor.right = current
current = current.left
else
predecessor.right = null
visit(current)
current = current.right
Explain
- When you find the
predecessor
of thecurrent
, you add a link from thepredecessor
to thecurrent
, bypredecessor.right=current
(link 9).
Note here you use the right child, which makes it easy for you to later on(line 5).
-
The link will be used to go back to the root node of the left subtree(line 5).
-
The previous link causes a cycle, and we avoid the cycle in the
findPredecessor
function(line 7) by:
/* Find the inorder predecessor of current */
pre = current.left;
while (pre.right != null && pre.right != current)
pre = pre.right;
-
If predecessor.right is not null, it means the left sub-tree is processed. And you should process the right subtree now (
current = current.right
, line 14). -
How to process the left subtree? Iteratively go to the left most node(line 10), until the node does not have a left node, i.e. leaf (line 3).
-
When you go back to the root after process the left subtree, it is time to remove the link from the
predecessor
to thecurrent
. -
When to
visit(current)
decide whether it is an inorder, or preorder.
Code
Here we present an easy to remember code structure
/*
key idea is to find the predecessor, and link predecessor to the current node before going down further so we can come back
*/
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
TreeNode cur = root; // copy the root reference so we do not operate on the root
while (cur != null) {
// first find the predecessor
// which is the right most node of its left child
TreeNode pre = cur.left;
while (pre != null && pre.right != null && pre.right != cur) {
// pre != null to prevent that root.left is null
// pre.right != null so that we ends at the right most node
// pre.right != node so we do not enter the loop we created, and we process this later
pre = pre.right;
}
if (pre == null) {
// Case 1: no predecessor, which means cur.left is null
// it is time to process the cur
res.add(cur.val);
// then move to right
// note right can be null, and we will break the while loop
// go to right can also be jumping from predecessor to the next
cur = cur.right;
} else if (pre.right == null) {
// Case 2: pre.right is not yet linked to cur
// link pre.right to cur, so we can go back to cur after processing the left child-tree
pre.right = cur;
// process the left child-tree
// cur.left can not be null, otherwise pre will be null, and is processed in Case 1
cur = cur.left;
} else {
// Case 3: pre.right is linked to cur
// which means the left child-tree is processed
// we go back to cur (done automatically, as cur's pre is pointing to cur, and we are at cur now)
// and process cur
res.add(cur.val);
// reset pre link to recover the original tree structure
pre.right = null;
// process the right subtree
// go to right can also be jumping from predecessor to the next
cur = cur.right;
}
}
return res;
}
Time Complexity
How is the time complexity of Morris Traversal o(n)?