Skip to content

Commit 6963674

Browse files
authored
Create README.md
1 parent 9e2e8aa commit 6963674

File tree

1 file changed

+365
-0
lines changed
  • 17 - Binary Tree Data Structure Problems/31 - Binary Tree From In-Order & Post-Order

1 file changed

+365
-0
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
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+
![image](https://github.com/user-attachments/assets/7a3b27b3-633f-4081-86b2-73824ce1b7a4)
8+
![image](https://github.com/user-attachments/assets/fb98adbf-7f1b-4467-b573-5a07945bc4f4)
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

Comments
 (0)