|
| 1 | +<h1 align='center'>Binary - Tree - From - In-Order & Post-Order</h1> |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +**Problem URL :** [Binary Tree From In-Order & Post-Order](https://www.geeksforgeeks.org/problems/tree-from-postorder-and-inorder/1?itm_source=geeksforgeeks&itm_medium=article&itm_campaign=practice_card) |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +## Problem Explanation |
| 11 | +Given the inorder and postorder traversals of a binary tree, the goal is to reconstruct the original binary tree. |
| 12 | + |
| 13 | +### Input |
| 14 | +- **Inorder Traversal**: This traversal visits nodes in the left-root-right order. |
| 15 | +- **Postorder Traversal**: This traversal visits nodes in the left-right-root order. |
| 16 | + |
| 17 | +### Output |
| 18 | +Return the root of the constructed binary tree. |
| 19 | + |
| 20 | +### Example |
| 21 | +For the following inorder and postorder traversals: |
| 22 | +- **Inorder**: `[9, 3, 15, 20, 7]` |
| 23 | +- **Postorder**: `[9, 15, 7, 20, 3]` |
| 24 | + |
| 25 | +The corresponding binary tree can be represented as: |
| 26 | +``` |
| 27 | + 3 |
| 28 | + / \ |
| 29 | + 9 20 |
| 30 | + / \ |
| 31 | + 15 7 |
| 32 | +``` |
| 33 | + |
| 34 | +### Edge Cases |
| 35 | +1. **Empty Trees**: If both traversals are empty, return `NULL`. |
| 36 | +2. **Single Node**: If the tree has only one node, the traversals will contain that node only. |
| 37 | + |
| 38 | +## Step 2: Approach |
| 39 | + |
| 40 | +### High-Level Overview |
| 41 | +To construct the tree, we can utilize the properties of the inorder and postorder traversals: |
| 42 | +- The last element in postorder is always the root of the tree. |
| 43 | +- Using the root, we can find the position in the inorder array to divide the left and right subtrees. |
| 44 | + |
| 45 | +### Step-by-Step Breakdown |
| 46 | +1. **Mapping Indices**: Create a mapping of values to their indices from the inorder traversal for quick look-up. |
| 47 | +2. **Recursive Function**: Create a recursive function that: |
| 48 | + - Takes the current root value (from postorder). |
| 49 | + - Finds its position in inorder to determine left and right subtree elements. |
| 50 | + - Recursively constructs the right and left subtrees. |
| 51 | + |
| 52 | +### Pseudocode |
| 53 | +```plaintext |
| 54 | +function buildTree(inorder, postorder): |
| 55 | + create a mapping of inorder indices |
| 56 | + call recursive function solve with initial parameters |
| 57 | + return the constructed tree |
| 58 | +
|
| 59 | +function solve(inorder, postorder, index): |
| 60 | + if index is less than 0 or bounds are invalid: |
| 61 | + return NULL |
| 62 | + create a new node with postorder[index] |
| 63 | + decrement index |
| 64 | + find position in inorder using the mapping |
| 65 | + recursively construct right subtree |
| 66 | + recursively construct left subtree |
| 67 | + return the constructed node |
| 68 | +``` |
| 69 | + |
| 70 | +## Problem Solution |
| 71 | +```cpp |
| 72 | +class Solution { |
| 73 | + public: |
| 74 | + void createMapping(vector<int> &in, map<int, int> &nodeToIndex, int n){ |
| 75 | + for(int i = 0; i < n; i++){ |
| 76 | + nodeToIndex[in[i]] = i; |
| 77 | + } |
| 78 | + |
| 79 | + } |
| 80 | + |
| 81 | + Node* solve(vector<int>&in , vector<int> postorder, int &index, int inOrderStart, int inOrderEnd, int n, map<int, int> &nodeToIndex){ |
| 82 | + if(index < 0 || inOrderStart > inOrderEnd) return NULL; |
| 83 | + |
| 84 | + int element = postorder[index--]; |
| 85 | + Node* root = new Node(element); |
| 86 | + |
| 87 | + int position = nodeToIndex[element]; |
| 88 | + |
| 89 | + root->right = solve(in, postorder, index, position + 1, inOrderEnd, n, nodeToIndex); |
| 90 | + root->left = solve(in, postorder, index, inOrderStart, position - 1, n, nodeToIndex); |
| 91 | + |
| 92 | + return root; |
| 93 | + } |
| 94 | + // Function to return a tree created from postorder and inoreder traversals. |
| 95 | + Node* buildTree(vector<int> inorder, vector<int> &postorder) { |
| 96 | + int n = postorder.size(); |
| 97 | + |
| 98 | + int postOrderIndex = n-1; |
| 99 | + map<int, int> nodeToIndex; |
| 100 | + createMapping(inorder, nodeToIndex, n); |
| 101 | + |
| 102 | + return solve(inorder, postorder, postOrderIndex, 0, n-1, n, nodeToIndex); |
| 103 | + } |
| 104 | +}; |
| 105 | +``` |
| 106 | +
|
| 107 | +## Problem Solution Explanation |
| 108 | +Certainly! Let’s delve deeper into the provided C++ source code for constructing a binary tree from its inorder and postorder traversals. We will explain each line in detail and provide examples where necessary. |
| 109 | +
|
| 110 | +```cpp |
| 111 | +class Solution { |
| 112 | +``` |
| 113 | +This line defines a class named `Solution`. In competitive programming and coding interviews, it’s common to encapsulate the solution to a problem within a class. |
| 114 | + |
| 115 | +```cpp |
| 116 | +public: |
| 117 | +``` |
| 118 | +This keyword indicates that the following members (functions or variables) can be accessed from outside the class. |
| 119 | + |
| 120 | +### Function: createMapping |
| 121 | + |
| 122 | +```cpp |
| 123 | +void createMapping(vector<int> &in, map<int, int> &nodeToIndex, int n){ |
| 124 | +``` |
| 125 | +This function creates a mapping of node values to their indices in the inorder traversal. |
| 126 | +- **Parameters**: |
| 127 | + - `vector<int> &in`: A reference to the vector containing the inorder traversal. |
| 128 | + - `map<int, int> &nodeToIndex`: A reference to a map that will store each node value and its corresponding index. |
| 129 | + - `int n`: The number of nodes (or size of the inorder array). |
| 130 | +
|
| 131 | +```cpp |
| 132 | + for(int i = 0; i < n; i++){ |
| 133 | +``` |
| 134 | +This initiates a loop that iterates through all elements in the inorder array. |
| 135 | + |
| 136 | +```cpp |
| 137 | + nodeToIndex[in[i]] = i; // Map value to its index |
| 138 | +``` |
| 139 | +Here, each value in the inorder array (`in[i]`) is mapped to its index (`i`) in the `nodeToIndex` map. This allows for quick lookups of the index of any node value during the tree construction process. |
| 140 | + |
| 141 | +### Example for createMapping |
| 142 | +For the inorder traversal `[9, 3, 15, 20, 7]`, the map after execution will look like: |
| 143 | +``` |
| 144 | +{ |
| 145 | + 9: 0, |
| 146 | + 3: 1, |
| 147 | + 15: 2, |
| 148 | + 20: 3, |
| 149 | + 7: 4 |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +### Function: solve |
| 154 | + |
| 155 | +```cpp |
| 156 | +Node* solve(vector<int>& in, vector<int> postorder, int &index, int inOrderStart, int inOrderEnd, int n, map<int, int> &nodeToIndex){ |
| 157 | +``` |
| 158 | +This is a recursive function that constructs the binary tree. |
| 159 | +- **Parameters**: |
| 160 | + - `vector<int>& in`: The inorder traversal vector. |
| 161 | + - `vector<int> postorder`: The postorder traversal vector. |
| 162 | + - `int &index`: A reference to the index of the current root in the postorder traversal (it will decrement as we process). |
| 163 | + - `int inOrderStart`: The starting index of the current subtree in the inorder traversal. |
| 164 | + - `int inOrderEnd`: The ending index of the current subtree in the inorder traversal. |
| 165 | + - `int n`: The number of nodes. |
| 166 | + - `map<int, int>& nodeToIndex`: The map created to find indices of inorder elements. |
| 167 | +
|
| 168 | +```cpp |
| 169 | + if(index < 0 || inOrderStart > inOrderEnd) return NULL; // Base case |
| 170 | +``` |
| 171 | +This checks for two base cases: |
| 172 | +- If the index is negative, it means we have processed all nodes in postorder. |
| 173 | +- If the starting index of the inorder segment is greater than the ending index, it indicates that there are no nodes to process in this subtree. |
| 174 | + |
| 175 | +If either condition is true, the function returns `NULL`, indicating no node can be created. |
| 176 | + |
| 177 | +### Example for Base Case |
| 178 | +- If `index = -1` or `inOrderStart > inOrderEnd`, the function returns `NULL`. |
| 179 | + |
| 180 | +```cpp |
| 181 | + int element = postorder[index--]; // Get the current root from postorder |
| 182 | +``` |
| 183 | +The current root is fetched from the `postorder` array using the `index`. After retrieving, the index is decremented to move to the next root for subsequent recursive calls. |
| 184 | + |
| 185 | +### Example |
| 186 | +For `postorder = [9, 15, 7, 20, 3]` and starting with `index = 4`, the first root fetched will be `3`, and the index will then become `3`. |
| 187 | + |
| 188 | +```cpp |
| 189 | + Node* root = new Node(element); // Create a new node for this root |
| 190 | +``` |
| 191 | +A new `Node` is created using the fetched root value. The constructor `Node(element)` initializes the node with the value. |
| 192 | + |
| 193 | +```cpp |
| 194 | + int position = nodeToIndex[element]; // Find the position in inorder |
| 195 | +``` |
| 196 | +This line finds the index of the current root in the `inorder` array using the previously created map. This index will help divide the inorder array into left and right subtrees. |
| 197 | + |
| 198 | +### Example |
| 199 | +Continuing from the previous example, `position` for `element = 3` will be `1`, indicating that `3` is at index `1` in the inorder traversal. |
| 200 | + |
| 201 | +```cpp |
| 202 | + // Recursively construct right subtree first, then left subtree |
| 203 | + root->right = solve(in, postorder, index, position + 1, inOrderEnd, n, nodeToIndex); |
| 204 | +``` |
| 205 | +This line recursively constructs the right subtree of the current root. The new parameters are: |
| 206 | +- `inOrderStart` is now `position + 1` (to process elements after the current root). |
| 207 | +- `inOrderEnd` remains unchanged, as it still indicates the end of the subtree. |
| 208 | + |
| 209 | +### Example |
| 210 | +For `element = 3`, `position + 1` will be `2`, so the right subtree is constructed from `inorder[2...4]`, which corresponds to `[15, 20, 7]`. |
| 211 | + |
| 212 | +```cpp |
| 213 | + root->left = solve(in, postorder, index, inOrderStart, position - 1, n, nodeToIndex); |
| 214 | +``` |
| 215 | +This line constructs the left subtree with: |
| 216 | +- `inOrderStart` remains unchanged. |
| 217 | +- `inOrderEnd` is now `position - 1` (to process elements before the current root). |
| 218 | + |
| 219 | +### Example |
| 220 | +Continuing from the example, for `element = 3`, `position - 1` will be `0`, so the left subtree is constructed from `inorder[0...0]`, which corresponds to `[9]`. |
| 221 | + |
| 222 | +```cpp |
| 223 | + return root; // Return the constructed node |
| 224 | +``` |
| 225 | +Finally, the function returns the newly created node, which can be used as the root for its subtrees in the previous recursive calls. |
| 226 | + |
| 227 | +### Main Function: buildTree |
| 228 | + |
| 229 | +```cpp |
| 230 | +Node* buildTree(vector<int> inorder, vector<int> &postorder) { |
| 231 | +``` |
| 232 | +This function serves as the main entry point to construct the tree from the provided traversals. |
| 233 | +- **Parameters**: |
| 234 | + - `vector<int> inorder`: The inorder traversal array. |
| 235 | + - `vector<int> &postorder`: The postorder traversal array. |
| 236 | +
|
| 237 | +```cpp |
| 238 | + int n = postorder.size(); // Size of the postorder array |
| 239 | +``` |
| 240 | +Calculates the number of nodes based on the size of the postorder array. The number of nodes is the same as the size of either traversal. |
| 241 | + |
| 242 | +```cpp |
| 243 | + int postOrderIndex = n - 1; // Initialize index for postorder |
| 244 | +``` |
| 245 | +Sets the initial index for postorder to the last element, which will be the first root processed. |
| 246 | + |
| 247 | +```cpp |
| 248 | + map<int, int> nodeToIndex; // Map to store the index of inorder elements |
| 249 | +``` |
| 250 | +Declares a map to store the indices of the inorder elements. |
| 251 | + |
| 252 | +```cpp |
| 253 | + createMapping(inorder, nodeToIndex, n); // Create the mapping |
| 254 | +``` |
| 255 | +Calls the `createMapping` function to fill the map with indices of the inorder elements. |
| 256 | +
|
| 257 | +```cpp |
| 258 | + return solve(inorder, postorder, postOrderIndex, 0, n - 1, n, nodeToIndex); // Start solving |
| 259 | +``` |
| 260 | +Finally, it calls the `solve` function to start the recursive process to build the tree, and returns the root node of the constructed tree. |
| 261 | + |
| 262 | +## Example Walkthrough |
| 263 | +Let’s put it all together with the example: |
| 264 | + |
| 265 | +### Input |
| 266 | +```plaintext |
| 267 | +Inorder: [9, 3, 15, 20, 7] |
| 268 | +Postorder: [9, 15, 7, 20, 3] |
| 269 | +``` |
| 270 | + |
| 271 | +1. **Mapping Creation**: |
| 272 | + - After calling `createMapping`, the map will be: |
| 273 | + ``` |
| 274 | + { |
| 275 | + 9: 0, |
| 276 | + 3: 1, |
| 277 | + 15: 2, |
| 278 | + 20: 3, |
| 279 | + 7: 4 |
| 280 | + } |
| 281 | + ``` |
| 282 | +
|
| 283 | +2. **First Call to solve**: |
| 284 | + - `element = 3`, creates `Node(3)`, finds `position = 1`. |
| 285 | + - Calls to construct right subtree with `inorder[2...4]` (subarray: `[15, 20, 7]`). |
| 286 | +
|
| 287 | +3. **Second Call to solve** (right subtree of `3`): |
| 288 | + - `element = 20`, creates `Node(20)`, finds `position = 3`. |
| 289 | + - Calls to construct right subtree with `inorder[4...4]` (subarray: `[7]`). |
| 290 | +
|
| 291 | +4. **Third Call to solve** (right subtree of `20`): |
| 292 | + - `element = 7`, creates `Node(7)`, finds `position = 4`. |
| 293 | + - Base case reached for both left and right, returns `NULL`. |
| 294 | +
|
| 295 | +5. **Backtracking**: |
| 296 | + - Constructs left subtree of `20` (no nodes, returns `NULL`). |
| 297 | + - Returns to the root node `3`, constructs left subtree with `inorder[0...0]` (subarray: `[9]`). |
| 298 | +
|
| 299 | +6. **Fourth Call to solve** (left subtree of `3`): |
| 300 | + - `element = 9`, creates `Node(9)`, finds `position = 0`. |
| 301 | + - Base case reached for both left and right, returns `NULL`. |
| 302 | +
|
| 303 | +7. |
| 304 | +
|
| 305 | + **Final Structure**: |
| 306 | + - The final binary tree structure will be: |
| 307 | + ``` |
| 308 | + 3 |
| 309 | + / \ |
| 310 | + 9 20 |
| 311 | + / \ |
| 312 | + 15 7 |
| 313 | + ``` |
| 314 | +
|
| 315 | +
|
| 316 | +### Example 1 |
| 317 | +**Input**: |
| 318 | +```plaintext |
| 319 | +Inorder: [9, 3, 15, 20, 7] |
| 320 | +Postorder: [9, 15, 7, 20, 3] |
| 321 | +``` |
| 322 | +**Output**: |
| 323 | +The constructed binary tree will have `3` as the root, `9` as the left child, and `20` as the right child, which further has `15` and `7` as its children. |
| 324 | + |
| 325 | +### Example 2 |
| 326 | +**Input**: |
| 327 | +```plaintext |
| 328 | +Inorder: [] |
| 329 | +Postorder: [] |
| 330 | +``` |
| 331 | +**Output**: |
| 332 | +Returns `NULL` as the tree is empty. |
| 333 | + |
| 334 | +### Example 3 |
| 335 | +**Input**: |
| 336 | +```plaintext |
| 337 | +Inorder: [1] |
| 338 | +Postorder: [1] |
| 339 | +``` |
| 340 | +**Output**: |
| 341 | +A single node tree with `1` as the root. |
| 342 | + |
| 343 | +### Explanation of Outputs |
| 344 | +The outputs validate that the function correctly reconstructs the binary tree from the given traversals. |
| 345 | + |
| 346 | +## Step 5: Time and Space Complexity |
| 347 | + |
| 348 | +### Time Complexity |
| 349 | +- **Mapping Creation**: O(n) for creating the mapping from inorder traversal. |
| 350 | +- **Tree Construction**: O(n) as we visit each node once during the recursive calls. |
| 351 | +- **Total Time Complexity**: O(n), where n is the number of nodes. |
| 352 | + |
| 353 | +### Space Complexity |
| 354 | +- **Space for Mapping**: O(n) for storing the indices of inorder elements. |
| 355 | +- **Recursion Stack**: O(h) for the call stack in case of recursion, where h is the height of the tree. |
| 356 | +- **Total Space Complexity**: O(n) in the worst case for skewed trees, or O(h) in balanced cases. |
| 357 | + |
| 358 | +### Optimizations |
| 359 | +This approach is efficient and optimal for reconstructing the binary tree with the given traversals. Any further optimizations would typically involve reducing function call overhead or minimizing space usage, but the current method strikes a balance between clarity and efficiency. |
| 360 | + |
| 361 | +## Additional Tips |
| 362 | +- **Error Handling**: Always check for edge cases like empty inputs and handle them gracefully. |
| 363 | +- **Testing**: Use various test cases to ensure robustness, especially with edge cases. |
| 364 | + |
| 365 | +This breakdown provides a comprehensive understanding of the problem and solution, making it accessible for beginners to follow along and learn. |
0 commit comments