|
| 1 | +<h1 align='center'>Morris - Traversal - for - Binary Trees - In-order Traversal</h1> |
| 2 | + |
| 3 | +Morris Traversal is a method for traversing binary trees without using extra space, like recursion stacks or auxiliary stacks, by temporarily modifying the tree's structure. It allows for an **in-order traversal** (left-root-right) with **O(1) space complexity**. Let's break down Morris Traversal in detail and examine each part of the approach, the code, and its efficiency. |
| 4 | + |
| 5 | +### 1. Detailed Explanation of Morris Traversal |
| 6 | + |
| 7 | +The Morris Traversal algorithm leverages a key insight: we can create temporary links in the tree to remember the path back up. Specifically, for any node, we connect its **in-order predecessor** (the rightmost node in its left subtree) back to the node itself. This link lets us return to the node without needing a stack. |
| 8 | + |
| 9 | +Here are the essential steps of Morris Traversal: |
| 10 | + |
| 11 | +1. **Start at the Root**: Begin traversal from the root node and use a pointer `current` to navigate the tree. |
| 12 | + |
| 13 | +2. **Process Each Node**: |
| 14 | + - While `current` is not null: |
| 15 | + - **If the Left Child is Null**: |
| 16 | + - If `current` has no left child, there is no left subtree to explore, so we simply process `current` (print it or add it to our result list) and move to the right child. |
| 17 | + |
| 18 | + - **If the Left Child is Not Null**: |
| 19 | + - If `current` has a left child, find its in-order predecessor in the left subtree: |
| 20 | + - The **in-order predecessor** is the rightmost node in the left subtree, meaning the node with the largest value in the left subtree of `current`. |
| 21 | + - **Create or Remove Link**: |
| 22 | + - **If the predecessor’s right pointer is null**, create a temporary link from the predecessor back to `current`. This link allows us to return to `current` after finishing the left subtree. Then, move `current` to its left child to continue traversing the left subtree. |
| 23 | + - **If the predecessor’s right pointer is already pointing to `current`**, it means we have finished processing the left subtree. In this case, remove the link by setting the predecessor's right pointer to null, process `current`, and move to the right child. |
| 24 | + |
| 25 | +3. **End of Traversal**: Repeat the process until `current` becomes null, indicating that the entire tree has been visited. |
| 26 | + |
| 27 | +### 2. Approach of Morris Traversal |
| 28 | + |
| 29 | +1. **Initialization**: |
| 30 | + - Start with a pointer `current` at the root of the tree. |
| 31 | + |
| 32 | +2. **Handling Left Subtrees**: |
| 33 | + - If the node has a left child, find the node’s in-order predecessor and set up a temporary link back to the node. |
| 34 | + |
| 35 | +3. **Traversal and Link Removal**: |
| 36 | + - Once the left subtree is fully traversed, remove the temporary link and proceed to the right subtree. |
| 37 | + |
| 38 | +4. **Completion**: |
| 39 | + - Continue this until every node is visited exactly once, resulting in an in-order traversal. |
| 40 | + |
| 41 | +### 3. Code for Morris In-Order Traversal in C++ |
| 42 | + |
| 43 | +Here’s how Morris Traversal is implemented for in-order traversal in C++: |
| 44 | + |
| 45 | +```cpp |
| 46 | +#include <iostream> |
| 47 | +using namespace std; |
| 48 | + |
| 49 | +struct TreeNode { |
| 50 | + int val; |
| 51 | + TreeNode* left; |
| 52 | + TreeNode* right; |
| 53 | + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} |
| 54 | +}; |
| 55 | + |
| 56 | +void morrisInorderTraversal(TreeNode* root) { |
| 57 | + TreeNode* current = root; |
| 58 | + |
| 59 | + while (current != nullptr) { |
| 60 | + if (current->left == nullptr) { |
| 61 | + // If no left child, visit current and move to the right |
| 62 | + cout << current->val << " "; |
| 63 | + current = current->right; |
| 64 | + } else { |
| 65 | + // Find the in-order predecessor of current |
| 66 | + TreeNode* predecessor = current->left; |
| 67 | + while (predecessor->right != nullptr && predecessor->right != current) { |
| 68 | + predecessor = predecessor->right; |
| 69 | + } |
| 70 | + |
| 71 | + if (predecessor->right == nullptr) { |
| 72 | + // Establish temporary link from predecessor's right to current |
| 73 | + predecessor->right = current; |
| 74 | + current = current->left; |
| 75 | + } else { |
| 76 | + // Remove temporary link and visit current |
| 77 | + predecessor->right = nullptr; |
| 78 | + cout << current->val << " "; |
| 79 | + current = current->right; |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +int main() { |
| 86 | + // Example tree |
| 87 | + TreeNode* root = new TreeNode(1); |
| 88 | + root->left = new TreeNode(2); |
| 89 | + root->right = new TreeNode(3); |
| 90 | + root->left->left = new TreeNode(4); |
| 91 | + root->left->right = new TreeNode(5); |
| 92 | + |
| 93 | + cout << "Morris In-order Traversal: "; |
| 94 | + morrisInorderTraversal(root); // Expected Output: 4 2 5 1 3 |
| 95 | + return 0; |
| 96 | +} |
| 97 | +``` |
| 98 | +
|
| 99 | +
|
| 100 | +### 4. Explanation of Code |
| 101 | +
|
| 102 | +- **TreeNode Structure**: |
| 103 | + - Defines a binary tree node with an integer value (`val`) and pointers to the left and right children. |
| 104 | + |
| 105 | +- **morrisInorderTraversal Function**: |
| 106 | + - **Loop through the Tree**: Starts at the root and continues until all nodes are visited. |
| 107 | + - **Check Left Child**: |
| 108 | + - If `current->left` is null, print `current->val` (since there’s no left subtree) and move to `current->right`. |
| 109 | + - If `current->left` exists, find `current`’s predecessor (rightmost node in `current->left` subtree). |
| 110 | + - **Create/Remove Links**: |
| 111 | + - If `predecessor->right` is null, establish a link to `current` and move `current` to the left child. |
| 112 | + - If `predecessor->right` is `current` (link exists), remove it (set `predecessor->right` to null), print `current->val`, and move `current` to the right child. |
| 113 | + |
| 114 | +- **Output**: |
| 115 | + - This code traverses the tree in in-order (left-root-right) and prints the values of each node. |
| 116 | +
|
| 117 | +
|
| 118 | +### 5. Time and Space Complexity Analysis |
| 119 | +
|
| 120 | +- **Time Complexity**: |
| 121 | + - The time complexity is **O(n)**, where \( n \) is the number of nodes in the tree. Although each node is visited multiple times while finding predecessors and establishing/removing links, each link is created and removed exactly once. This makes the overall time complexity linear, as each operation is limited to a constant number of steps. |
| 122 | +
|
| 123 | +- **Space Complexity**: |
| 124 | + - The Morris Traversal algorithm has **O(1) space complexity** because it doesn’t use any additional data structures. Instead, it modifies the tree temporarily by creating links to the predecessor nodes, which are later removed, ensuring no additional space is used apart from the few pointers required by the function itself. |
0 commit comments