Skip to content

Commit 02453bc

Browse files
authored
Create README.md
1 parent 913beeb commit 02453bc

File tree

1 file changed

+258
-0
lines changed
  • 17 - Binary Tree Data Structure Problems/15 - Tree Boundary Traversal

1 file changed

+258
-0
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
<h1 align='center'>Tree - Boundary - Traversal</h1>
2+
3+
## Problem Statement
4+
5+
**Problem URL :** [Tree Boundary Traversal](https://www.geeksforgeeks.org/problems/boundary-traversal-of-binary-tree/1)
6+
7+
![image](https://github.com/user-attachments/assets/10595c19-4921-4c10-9af4-86fda9af2983)
8+
![image](https://github.com/user-attachments/assets/2a884702-0d1c-41fc-a35f-5dd0fe32574b)
9+
![image](https://github.com/user-attachments/assets/6780653a-5c25-4e48-8a42-dc316d56e56c)
10+
![image](https://github.com/user-attachments/assets/3232130f-e59a-442a-aa10-6b647b053a83)
11+
12+
## Problem Explanation
13+
14+
The goal is to traverse the boundary of a binary tree. The boundary of a tree consists of:
15+
1. The **left boundary** (excluding the leaves).
16+
2. The **leaf nodes** (from left to right).
17+
3. The **right boundary** (excluding the leaves and in reverse order).
18+
19+
### Example
20+
Given the binary tree:
21+
22+
```
23+
1
24+
/ \
25+
2 3
26+
/ \ \
27+
4 5 6
28+
/ \
29+
7 8
30+
```
31+
32+
**Boundary Traversal Output**:
33+
- **Left Boundary**: `1, 2` (from top to bottom, excluding leaves)
34+
- **Leaf Nodes**: `4, 7, 8, 6` (in left to right order)
35+
- **Right Boundary**: `3` (from bottom to top, excluding leaves)
36+
37+
**Final Result**: `[1, 2, 4, 7, 8, 6, 3]`
38+
39+
## Step 2: Approach
40+
41+
### Approach Explanation
42+
1. **Identify the Parts of the Boundary**:
43+
- **Left Boundary**: Traverse down the leftmost path, skipping leaf nodes.
44+
- **Leaf Nodes**: Traverse the entire tree and collect leaf nodes.
45+
- **Right Boundary**: Traverse down the rightmost path in reverse, skipping leaf nodes.
46+
47+
2. **Implementation**:
48+
- Create three helper functions to handle left boundary traversal, leaf node collection, and right boundary traversal.
49+
- Use a vector to store the result and return it after processing all parts.
50+
51+
### Beginner-Friendly Steps
52+
1. Start with the root node. If it's `NULL`, return an empty vector.
53+
2. Add the root's value to the result vector.
54+
3. Implement a recursive function to traverse the left boundary and skip leaf nodes.
55+
4. Implement another function to collect all leaf nodes by recursively checking left and right children.
56+
5. Implement the right boundary function to traverse the rightmost path, skipping leaf nodes, and store them in reverse order.
57+
6. Combine all the collected nodes in the final result.
58+
59+
```cpp
60+
class Solution {
61+
public:
62+
void traverseLeft(Node* root, vector<int> &ans){
63+
if(root == NULL || (root -> left == NULL && root -> right == NULL)) return;
64+
65+
ans.push_back(root -> data);
66+
67+
if(root -> left) traverseLeft(root -> left, ans);
68+
else traverseLeft(root -> right, ans);
69+
}
70+
71+
void traverseLeaf(Node* root, vector<int> &ans){
72+
if(root == NULL) return;
73+
74+
if (root->left == NULL && root->right == NULL) {
75+
ans.push_back(root->data);
76+
return;
77+
}
78+
79+
traverseLeaf(root -> left, ans);
80+
traverseLeaf(root -> right, ans);
81+
82+
}
83+
84+
void traverseRight(Node* root, vector<int> &ans){
85+
if(root == NULL || (root -> left == NULL && root -> right == NULL)) return;
86+
87+
88+
if(root -> right) traverseRight(root -> right, ans);
89+
else traverseRight(root -> left, ans);
90+
91+
ans.push_back(root -> data);
92+
}
93+
94+
95+
vector <int> boundary(Node *root)
96+
{
97+
vector<int> ans;
98+
if(root == NULL) return ans;
99+
100+
ans.push_back(root -> data);
101+
102+
traverseLeft(root -> left, ans);
103+
104+
traverseLeaf(root -> left, ans);
105+
traverseLeaf(root -> right, ans);
106+
107+
traverseRight(root -> right, ans);
108+
109+
return ans;
110+
}
111+
};
112+
```
113+
114+
## Problem Solution Explanation
115+
Let's break down the code line by line:
116+
117+
```cpp
118+
class Solution {
119+
public:
120+
void traverseLeft(Node* root, vector<int> &ans){
121+
```
122+
- **Line 1**: Defines the class `Solution` and declares the method `traverseLeft` to traverse the left boundary of the tree.
123+
- **Line 2**: Takes a pointer to the root node of the subtree and a reference to a vector `ans` to store results.
124+
125+
```cpp
126+
if(root == NULL || (root -> left == NULL && root -> right == NULL)) return;
127+
```
128+
- **Line 3**: Checks if the current node is `NULL` or if it's a leaf node (both children are `NULL`). If true, the function returns since there's nothing to add.
129+
130+
### Example:
131+
- If `root` points to a leaf node like `4`, the function returns without adding anything to `ans`.
132+
133+
```cpp
134+
ans.push_back(root -> data);
135+
```
136+
- **Line 4**: Adds the current node's data to the `ans` vector, as it’s part of the left boundary.
137+
138+
### Example:
139+
- For node `2`, `ans` becomes `[2]`.
140+
141+
```cpp
142+
if(root -> left) traverseLeft(root -> left, ans);
143+
else traverseLeft(root -> right, ans);
144+
```
145+
- **Lines 5-6**: If there’s a left child, recursively call `traverseLeft` on the left child. If there’s no left child, call it on the right child.
146+
147+
### Example:
148+
- From `2`, it goes to `4` (the left child). If it were `3`, it would have traversed the right child instead.
149+
150+
```cpp
151+
void traverseLeaf(Node* root, vector<int> &ans){
152+
```
153+
- **Line 8**: Declares the `traverseLeaf` function to collect leaf nodes.
154+
155+
```cpp
156+
if(root == NULL) return;
157+
```
158+
- **Line 9**: Checks if the current node is `NULL`. If true, returns.
159+
160+
```cpp
161+
if (root->left == NULL && root->right == NULL) {
162+
ans.push_back(root->data);
163+
return;
164+
}
165+
```
166+
- **Lines 10-12**: Checks if the current node is a leaf. If so, adds it to `ans` and returns.
167+
168+
### Example:
169+
- For node `4`, it gets added to `ans`: `ans = [2, 4]`.
170+
171+
```cpp
172+
traverseLeaf(root -> left, ans);
173+
traverseLeaf(root -> right, ans);
174+
```
175+
- **Lines 13-14**: Recursively calls `traverseLeaf` on the left and right children.
176+
177+
### Example:
178+
- From `2`, it will recursively check `4`, `5`, and subsequently `7` and `8`.
179+
180+
```cpp
181+
void traverseRight(Node* root, vector<int> &ans){
182+
```
183+
- **Line 16**: Declares the `traverseRight` function for traversing the right boundary.
184+
185+
```cpp
186+
if(root == NULL || (root -> left == NULL && root -> right == NULL)) return;
187+
```
188+
- **Line 17**: Checks if the current node is `NULL` or a leaf node. If so, it returns.
189+
190+
```cpp
191+
if(root -> right) traverseRight(root -> right, ans);
192+
else traverseRight(root -> left, ans);
193+
```
194+
- **Lines 18-19**: First, traverses the right child. If it doesn’t exist, it goes to the left child.
195+
196+
### Example:
197+
- For node `3`, it goes right to `6`, adding `3` later after the recursive calls return.
198+
199+
```cpp
200+
ans.push_back(root -> data);
201+
```
202+
- **Line 20**: Adds the current node’s data to the `ans` vector after the recursive calls have returned (this ensures it adds in reverse order).
203+
204+
### Example:
205+
- After visiting `6`, it adds `3` to `ans`: `ans = [2, 4, 7, 8, 6, 3]` by the end.
206+
207+
```cpp
208+
vector <int> boundary(Node *root)
209+
{
210+
vector<int> ans; // Result vector to hold the boundary values.
211+
if(root == NULL) return ans; // If root is NULL, return empty result.
212+
```
213+
- **Line 22-24**: Declares the main function `boundary`, initializes an empty vector `ans`, and checks if `root` is `NULL`. If true, it returns `ans`.
214+
215+
```cpp
216+
ans.push_back(root -> data); // Add the root's data to the result.
217+
```
218+
- **Line 25**: Adds the root's data to `ans` as it is part of the boundary.
219+
220+
### Example:
221+
- If the root is `1`, `ans` becomes: `ans = [1]`.
222+
223+
```cpp
224+
traverseLeft(root -> left, ans);
225+
```
226+
- **Line 26**: Calls `traverseLeft` on the left child of the root to collect the left boundary.
227+
228+
```cpp
229+
traverseLeaf(root -> left, ans);
230+
traverseLeaf(root -> right, ans);
231+
```
232+
- **Lines 27-28**: Calls `traverseLeaf` on both left and right subtrees to collect all leaf nodes.
233+
234+
```cpp
235+
traverseRight(root -> right, ans);
236+
```
237+
- **Line 29**: Calls `traverseRight` on the right child of the root to collect the right boundary.
238+
239+
```cpp
240+
return ans; // Return the final boundary traversal.
241+
```
242+
- **Line 30**: Returns the completed `ans` vector containing the boundary values.
243+
244+
## Step 4: Time and Space Complexity
245+
246+
### Time Complexity
247+
- **Traversal Functions**:
248+
- Each of the three traversal functions (`traverseLeft`, `traverseLeaf`, `traverseRight`) will visit every node in the tree once.
249+
- Thus, the overall time complexity is **O(n)**, where `n` is the number of nodes in the tree.
250+
251+
### Space Complexity
252+
- **Space Complexity**:
253+
- The space complexity is primarily due to the recursion stack used in the traversal functions. In the worst case (e.g., a skewed tree), the maximum depth of recursion can be `n`.
254+
- However, since we're using a vector to store the results, the overall space complexity is **O(h)** for the recursion stack and **O(n)** for storing the boundary nodes in the result vector, where `h` is the height of the tree.
255+
256+
In summary, the time complexity is **O(n)**, and the space complexity is **O(n)** due to the storage of boundary nodes and potential recursion depth.
257+
258+
This comprehensive explanation should help you understand the problem, the approach taken, and how the provided code works in detail!

0 commit comments

Comments
 (0)