Discover how to walk through a tree without carrying any extra memory--like magic!
Why Morris Traversal Inorder Without Stack in DSA Rust?
Imagine you want to read all the pages of a book, but you can only hold one page at a time and have no bookmark to remember where you left off.
You try to flip back and forth manually, but it's confusing and slow.
Using a manual method like a stack to remember pages means extra memory and effort.
It's like carrying a pile of bookmarks, which can get lost or mixed up.
This slows you down and makes the process error-prone.
Morris Traversal cleverly uses the book's own pages to mark where to return next, without extra bookmarks.
It temporarily changes the book's structure to remember the path, then restores it perfectly.
This way, you can read all pages in order without extra memory or confusion.
unsafe fn inorder_traversal(root: *mut TreeNode) {
let mut stack: Vec<*mut TreeNode> = Vec::new();
let mut current = root;
while !current.is_null() || !stack.is_empty() {
while !current.is_null() {
stack.push(current);
current = (*current).left;
}
current = *stack.last().unwrap_or(&std::ptr::null_mut());
stack.pop();
println!("{}", (*current).val);
current = (*current).right;
}
}unsafe fn morris_inorder_traversal(root: *mut TreeNode) {
let mut current = root;
while !current.is_null() {
if (*current).left.is_null() {
println!("{}", (*current).val);
current = (*current).right;
} else {
let mut predecessor = (*current).left;
while !(*predecessor).right.is_null() && (*(*predecessor).right).val != (*current).val {
predecessor = (*predecessor).right;
}
if (*predecessor).right.is_null() {
(*predecessor).right = current;
current = (*current).left;
} else {
(*predecessor).right = std::ptr::null_mut();
println!("{}", (*current).val);
current = (*current).right;
}
}
}
}This method enables traversing a tree in order using no extra memory, making it efficient and elegant.
Think of exploring a maze where you mark your path by temporarily placing stones, then remove them as you backtrack, so you never get lost and don't carry extra tools.
Morris Traversal avoids extra memory by temporarily modifying the tree.
It restores the tree after traversal, keeping data intact.
This method is efficient for memory-limited environments.
Practice
Solution
Step 1: Understand Morris Traversal space usage
Morris Traversal modifies tree links temporarily to avoid using stack or recursion.Step 2: Compare with traditional methods
Traditional inorder traversal uses stack or recursion which require extra space. Morris Traversal uses O(1) extra space.Final Answer:
It uses no extra space like stack or recursion -> Option DQuick Check:
Space complexity = O(1) [OK]
- Thinking Morris is faster in time complexity
- Assuming it only works on BSTs
- Confusing it with recursive traversal
Solution
Step 1: Identify mutable pointer requirement
Morris Traversal modifies tree links, so mutable access is needed.Step 2: Choose correct Rust method
root.as_mut() gives mutable reference, root.as_ref() is immutable, clone copies, &root is immutable reference.Final Answer:
let mut current = root.as_mut(); -> Option BQuick Check:
Mutable reference needed = let mut current = root.as_mut(); [OK]
- Using as_ref() which is immutable
- Cloning unnecessarily
- Using immutable references when mutation is needed
struct Node {
val: i32,
left: Option,
right: Option,
}
fn morris_inorder(root: &mut Option) {
let mut current = root.as_mut();
while let Some(node) = current {
if node.left.is_none() {
print!("{} ", node.val);
current = node.right.as_mut();
} else {
let mut pre = node.left.as_mut();
while let Some(p) = pre {
if p.right.is_some() && p.right.as_ref().unwrap().val != node.val {
pre = p.right.as_mut();
} else {
break;
}
}
if pre.is_some() && pre.as_ref().unwrap().right.is_none() {
pre.as_mut().unwrap().right = Some(Box::new(Node { val: node.val, left: None, right: None }));
current = node.left.as_mut();
} else {
pre.as_mut().unwrap().right = None;
print!("{} ", node.val);
current = node.right.as_mut();
}
}
}
}
Solution
Step 1: Understand tree structure
Root=1, left=2, right=3. Inorder is left-root-right.Step 2: Trace Morris traversal output
Visit left (2), then root (1), then right (3). So output is "2 1 3".Final Answer:
2 1 3 -> Option AQuick Check:
Inorder traversal = 2 1 3 [OK]
- Confusing preorder or postorder output
- Not restoring links causing infinite loops
- Misreading tree structure
fn morris_inorder_bug(root: &mut Option) {
let mut current = root.as_mut();
while let Some(node) = current {
if node.left.is_some() {
let mut pre = node.left.as_mut();
while pre.is_some() && pre.as_ref().unwrap().right.is_some() {
pre = pre.unwrap().right.as_mut();
}
if pre.is_some() {
pre.as_mut().unwrap().right = Some(Box::new(Node { val: node.val, left: None, right: None }));
current = node.left.as_mut();
}
} else {
print!("{} ", node.val);
current = node.right.as_mut();
}
}
}
Solution
Step 1: Analyze threaded link handling
Code creates temporary right links but never removes them.Step 2: Understand infinite loop cause
Without removing threaded links, traversal cycles endlessly on same nodes.Final Answer:
Not removing temporary threaded links after use -> Option CQuick Check:
Threaded links must be removed to avoid loops [OK]
- Ignoring threaded link removal
- Confusing printing with traversal logic
- Not using mutable references properly
fn morris_inorder_collect(root: &mut Option) -> Vec<i32> {
let mut result = Vec::new();
let mut current = root.as_mut();
while let Some(node) = current {
if node.left.is_none() {
// Change here
current = node.right.as_mut();
} else {
let mut pre = node.left.as_mut();
while let Some(p) = pre {
if p.right.is_some() && p.right.as_ref().unwrap().val != node.val {
pre = p.right.as_mut();
} else {
break;
}
}
if pre.is_some() && pre.as_ref().unwrap().right.is_none() {
pre.as_mut().unwrap().right = Some(Box::new(Node { val: node.val, left: None, right: None }));
current = node.left.as_mut();
} else {
pre.as_mut().unwrap().right = None;
// Change here
current = node.right.as_mut();
}
}
}
result
}
Solution
Step 1: Identify where node values are processed
Values are printed at two places: when left is None and after restoring threaded links.Step 2: Replace printing with pushing to vector
To collect values, replace both prints with result.push(node.val) to keep traversal logic intact.Final Answer:
Replace both comments with result.push(node.val); -> Option AQuick Check:
Collect values by pushing at both print points [OK]
- Mixing print and push causing partial collection
- Pushing only once missing some nodes
- Breaking traversal by adding prints
Ready to prove it?
Pass the test and this pattern moves unknown
