📖 Scenario: You are working with a binary tree data structure. Normally, to visit all nodes in order (left, root, right), you use extra memory like a stack or recursion. But here, you want to do it without extra memory by changing the tree temporarily.This technique is called Morris Traversal. It helps save memory by using the tree's empty right pointers to remember where to go next.
🎯 Goal: Build a Rust program that creates a binary tree, then uses Morris Traversal to print the nodes in inorder sequence without using a stack or recursion.
📋 What You'll Learn
Create a binary tree node struct with integer values and optional left and right children
Build a sample binary tree with exactly these nodes and structure: root 1, left child 2, right child 3, left child of 2 is 4, right child of 2 is 5
Implement Morris Traversal inorder function that visits nodes without stack or recursion
Print the values of nodes in inorder sequence separated by spaces
💡 Why This Matters
🌍 Real World
Morris Traversal is useful in systems with limited memory where recursion or stacks are expensive, such as embedded devices or low-level system software.
💼 Career
Understanding Morris Traversal shows mastery of tree traversal algorithms and memory optimization, valuable for roles in systems programming, embedded software, and performance-critical applications.
Progress0 / 4 steps
1
Create the binary tree node struct and build the tree
Create a struct called TreeNode with fields val: i32, left: Option>, and right: Option>. Then create a variable root that builds this exact tree: 1 as root, 2 as left child of 1, 3 as right child of 1, 4 as left child of 2, and 5 as right child of 2.
DSA Rust
Hint
Use Option> for left and right to allow empty children.
Build the tree bottom-up inside Some(Box::new(TreeNode { ... })).
2
Add a mutable variable to hold the current node for traversal
Inside main, create a mutable variable called current and set it to root.as_deref_mut(). This will be used to traverse the tree.
DSA Rust
Hint
Use as_deref_mut() on root to get a mutable reference to the root node.
3
Implement Morris Traversal inorder loop
Write a while let Some(node) loop using current. Inside, if node.left is None, print node.val and move current to node.right.as_deref_mut(). Otherwise, find the inorder predecessor by moving to the rightmost node in node.left. If predecessor's right is None, set it to Some(node) and move current to node.left.as_deref_mut(). If predecessor's right is Some(node), reset it to None, print node.val, and move current to node.right.as_deref_mut(). Use print!("{} ", node.val) to print values.
DSA Rust
Hint
Use while let Some(node) = current to loop. Check if node.left is None. If yes, print and move right. Otherwise, find predecessor by going right until predecessor.right is None or points back to node.
Use std::ptr::eq to compare references for cycle detection.
4
Print the inorder traversal result
Add a final println!() after the traversal loop to print a newline. This completes the output of the inorder traversal.
DSA Rust
Hint
After the loop, add println!() to move to the next line for clean output.
Practice
(1/5)
1. What is the main advantage of Morris Traversal for inorder tree traversal compared to traditional methods?
easy
A. It is faster than all other traversal methods
B. It requires a special tree structure
C. It only works on binary search trees
D. It uses no extra space like stack or recursion
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 D
Quick Check:
Space complexity = O(1) [OK]
Hint: Remember Morris uses threaded links, no stack needed [OK]
Common Mistakes:
Thinking Morris is faster in time complexity
Assuming it only works on BSTs
Confusing it with recursive traversal
2. Which Rust code snippet correctly initializes a mutable pointer to the root node for Morris Traversal?
easy
A. let mut current = root.as_ref();
B. let mut current = root.as_mut();
C. let current = root.clone();
D. let current = &root;
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 B
Quick Check:
Mutable reference needed = let mut current = root.as_mut(); [OK]
Hint: Use as_mut() for mutable access in Rust trees [OK]
Common Mistakes:
Using as_ref() which is immutable
Cloning unnecessarily
Using immutable references when mutation is needed
3. Given the following Rust code for Morris Traversal on a binary tree with nodes 1 (root), 2 (left child), and 3 (right child), what is the printed inorder sequence?
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();
}
}
}
}
medium
A. 2 1 3
B. 1 2 3
C. 3 1 2
D. 1 3 2
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 A
Quick Check:
Inorder traversal = 2 1 3 [OK]
Hint: Inorder = left, root, right; Morris prints in this order [OK]
Common Mistakes:
Confusing preorder or postorder output
Not restoring links causing infinite loops
Misreading tree structure
4. In the following Rust snippet for Morris Traversal, what is the main bug causing an infinite loop?
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();
}
}
}
medium
A. Using immutable references instead of mutable
B. Incorrectly printing node values
C. Not removing temporary threaded links after use
D. Missing base case for empty tree
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 C
Quick Check:
Threaded links must be removed to avoid loops [OK]
Hint: Always remove threaded links after visiting left subtree [OK]
Common Mistakes:
Ignoring threaded link removal
Confusing printing with traversal logic
Not using mutable references properly
5. You want to modify Morris Traversal inorder code in Rust to collect node values into a vector instead of printing. Which change correctly implements this without breaking the traversal logic?
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
}
hard
A. Replace both comments with result.push(node.val);
B. Replace first comment with print!("{} ", node.val); second with result.push(node.val);
C. Replace first comment with result.push(node.val); second with print!("{} ", node.val);
D. Replace both comments with print!("{} ", node.val);
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 A
Quick Check:
Collect values by pushing at both print points [OK]
Hint: Push values where prints occur to collect inorder sequence [OK]
Common Mistakes:
Mixing print and push causing partial collection
Pushing only once missing some nodes
Breaking traversal by adding prints
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.