Skip to content

Commit cbe35bc

Browse files
authored
Create README.md
1 parent d2727b7 commit cbe35bc

File tree

1 file changed

+202
-0
lines changed
  • 17 - Binary Tree Data Structure Problems/26 - Lowest Common Ancestor of a Binary Tree

1 file changed

+202
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<h1 align='center'>Lowest - Common - Ancestor - Of a - Binary - Tree</h1>
2+
3+
## Problem Statement
4+
5+
**Problem URL :** [Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/)
6+
7+
![image](https://github.com/user-attachments/assets/76533e07-6eee-49b5-8e61-bc00b0481ab6)
8+
![image](https://github.com/user-attachments/assets/040cd0a6-73b0-40f6-9462-f92ffb2da5ae)
9+
10+
## Problem Explanation
11+
**Problem:** Given a binary tree, find the lowest common ancestor (LCA) of two given nodes, `p` and `q`. The LCA of two nodes in a binary tree is the lowest node that has both `p` and `q` as descendants (either direct children or via other nodes).
12+
13+
**Constraints:**
14+
- Each node in the tree has a unique value.
15+
- Both nodes `p` and `q` exist in the tree.
16+
17+
**Real-World Analogy:**
18+
Imagine a family tree where each node represents a person. The lowest common ancestor of two family members is the closest common ancestor, such as a grandparent or great-grandparent. Similarly, in the binary tree, the LCA is the nearest common node that `p` and `q` descend from.
19+
20+
**Edge Cases to Consider:**
21+
1. **Root node is `p` or `q`:** If `root` is one of the two nodes, then it is automatically the LCA.
22+
2. **Nodes in separate subtrees:** `p` and `q` may be located in different subtrees of the root.
23+
3. **Nodes are the same:** If `p` and `q` are the same node, that node is the LCA.
24+
25+
### Step 2: Approach
26+
27+
**High-Level Overview:**
28+
To find the LCA, we can use a recursive depth-first search (DFS) strategy:
29+
1. Traverse from the root to find `p` and `q`.
30+
2. When both `p` and `q` are found in separate subtrees of the same node, that node is the LCA.
31+
3. If `p` or `q` matches the root node, then root is their LCA.
32+
33+
**Detailed Steps:**
34+
1. **Base Case:** If the current `root` is `NULL`, return `NULL` since there is no node here.
35+
2. **Check for `p` or `q`:** If `root` is equal to `p` or `q`, return `root`.
36+
3. **Recursive Search:**
37+
- Recurse down the left and right subtrees to search for `p` and `q`.
38+
- If one node is found in each subtree, the current `root` is the LCA.
39+
4. **Return Results:**
40+
- If both left and right return non-`NULL` values, then `root` is the LCA.
41+
- If only one subtree returns a node, propagate that node up as the potential LCA.
42+
43+
**Pseudocode:**
44+
```plaintext
45+
function LCA(root, p, q):
46+
if root is NULL:
47+
return NULL
48+
49+
if root is p or root is q:
50+
return root
51+
52+
left = LCA(root.left, p, q)
53+
right = LCA(root.right, p, q)
54+
55+
if left is not NULL and right is not NULL:
56+
return root
57+
58+
return left if left is not NULL else right
59+
```
60+
## Problem Solution
61+
```cpp
62+
/**
63+
* Definition for a binary tree node.
64+
* struct TreeNode {
65+
* int val;
66+
* TreeNode *left;
67+
* TreeNode *right;
68+
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
69+
* };
70+
*/
71+
class Solution {
72+
public:
73+
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
74+
if(root == NULL) return NULL;
75+
76+
if(root -> val == p -> val || root -> val == q -> val) return root;
77+
78+
TreeNode* leftAns = lowestCommonAncestor(root -> left, p, q);
79+
TreeNode* rightAns = lowestCommonAncestor(root -> right, p, q);
80+
81+
if(leftAns != NULL && rightAns != NULL) return root;
82+
else if(leftAns != NULL && rightAns == NULL) return leftAns;
83+
else if(leftAns == NULL && rightAns != NULL) return rightAns;
84+
else return NULL;
85+
}
86+
};
87+
```
88+
89+
## Problem Solution Explanation
90+
91+
Here’s the code with line-by-line explanation:
92+
93+
1. **Function Definition and Parameters**
94+
```cpp
95+
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
96+
```
97+
- This function is a method of the `Solution` class.
98+
- It takes three parameters:
99+
- `root`: A pointer to the current node (starting with the root of the tree).
100+
- `p`: A pointer to one of the target nodes in the tree.
101+
- `q`: A pointer to the other target node.
102+
- The function will return a pointer to the node that is the lowest common ancestor of `p` and `q`.
103+
104+
2. **Line 1: Base Case**
105+
```cpp
106+
if (root == NULL) return NULL;
107+
```
108+
- **Purpose:** This line handles the base case. If `root` is `NULL` (i.e., we’ve reached a leaf node or an empty tree), it returns `NULL`.
109+
- **Reason:** If `root` is `NULL`, there’s no node here, so it can’t be the ancestor of `p` or `q`.
110+
111+
3. **Line 2: Check if `root` is a Target Node**
112+
```cpp
113+
if (root->val == p->val || root->val == q->val) return root;
114+
```
115+
- **Purpose:** Check if the current node (`root`) is equal to either `p` or `q`.
116+
- **Explanation:**
117+
- If the current `root` node matches the value of `p` or `q`, this node is either `p` or `q`.
118+
- Since one of our target nodes has been found, we return `root` as a potential candidate for the LCA.
119+
- **Example:** If `root` is `p`, this means `p` could potentially be the lowest common ancestor if `q` is found further down the tree.
120+
121+
4. **Line 3: Recursive Search in the Left Subtree**
122+
```cpp
123+
TreeNode* leftAns = lowestCommonAncestor(root->left, p, q);
124+
```
125+
- **Purpose:** Recursively search for nodes `p` and `q` in the left subtree.
126+
- **Explanation:**
127+
- This line makes a recursive call to `lowestCommonAncestor`, but with `root->left` as the new root node.
128+
- The result, `leftAns`, will be `NULL` if neither `p` nor `q` is found in the left subtree.
129+
- Otherwise, it will contain the pointer to `p`, `q`, or their common ancestor found in the left subtree.
130+
131+
5. **Line 4: Recursive Search in the Right Subtree**
132+
```cpp
133+
TreeNode* rightAns = lowestCommonAncestor(root->right, p, q);
134+
```
135+
- **Purpose:** Similar to the previous step, this line searches for nodes `p` and `q` in the right subtree.
136+
- **Explanation:**
137+
- A recursive call is made with `root->right` as the new root.
138+
- The result, `rightAns`, will store `NULL` if neither `p` nor `q` is found in the right subtree.
139+
- Otherwise, it contains the pointer to `p`, `q`, or their LCA within the right subtree.
140+
141+
6. **Line 5: Determine if Current `root` is the LCA**
142+
```cpp
143+
if (leftAns != NULL && rightAns != NULL) return root;
144+
```
145+
- **Purpose:** Check if nodes `p` and `q` were found in different subtrees of the current `root`.
146+
- **Explanation:**
147+
- If both `leftAns` and `rightAns` are non-`NULL`, then one target node was found in each subtree.
148+
- This means that the current node, `root`, is the lowest node that has both `p` and `q` as descendants.
149+
- Therefore, we return `root` as the lowest common ancestor.
150+
151+
7. **Line 6: Propagate the Left Result Upwards**
152+
```cpp
153+
else if (leftAns != NULL && rightAns == NULL) return leftAns;
154+
```
155+
- **Purpose:** If only `leftAns` is non-`NULL`, return `leftAns` as the LCA candidate.
156+
- **Explanation:**
157+
- If only `leftAns` is non-`NULL`, then either `p` or `q` (or their common ancestor) was found in the left subtree, while the right subtree has no relevant nodes.
158+
- We propagate `leftAns` upwards as a potential LCA since `p` and `q` could still be descendants of the node in `leftAns`.
159+
160+
8. **Line 7: Propagate the Right Result Upwards**
161+
```cpp
162+
else if (leftAns == NULL && rightAns != NULL) return rightAns;
163+
```
164+
- **Purpose:** Similarly, if only `rightAns` is non-`NULL`, return `rightAns`.
165+
- **Explanation:**
166+
- This means that either `p` or `q` (or their LCA) was found in the right subtree.
167+
- Since the left subtree has no relevant nodes, we return `rightAns` as the LCA candidate for the upper levels of recursion.
168+
169+
9. **Line 8: Neither `p` nor `q` Found**
170+
```cpp
171+
else return NULL;
172+
```
173+
- **Purpose:** If both `leftAns` and `rightAns` are `NULL`, return `NULL` as no LCA was found.
174+
- **Explanation:**
175+
- This means that neither `p` nor `q` is present in the current subtree rooted at `root`.
176+
- Returning `NULL` signifies that `p` and `q` are not in this subtree.
177+
178+
179+
### Step 4: Output Examples
180+
181+
1. **Example 1:**
182+
- **Input:** Binary tree root = [3,5,1,6,2,0,8,null,null,7,4], `p` = 5, `q` = 1
183+
- **Output:** 3
184+
- **Explanation:** Nodes 5 and 1 have the common ancestor 3, which is their LCA.
185+
186+
2. **Example 2:**
187+
- **Input:** Binary tree root = [3,5,1,6,2,0,8,null,null,7,4], `p` = 5, `q` = 4
188+
- **Output:** 5
189+
- **Explanation:** Since node 5 is an ancestor of node 4, 5 itself is the LCA.
190+
191+
3. **Example 3:**
192+
- **Input:** Binary tree root = [1,2], `p` = 1, `q` = 2
193+
- **Output:** 1
194+
- **Explanation:** Node 1 is the root and an ancestor of node 2, so it’s the LCA.
195+
196+
### Step 5: Time and Space Complexity
197+
198+
1. **Time Complexity:** \(O(N)\), where \(N\) is the number of nodes in the tree. In the worst case, each node is visited once to locate `p` and `q`.
199+
200+
2. **Space Complexity:** \(O(H)\), where \(H\) is the height of the tree. The recursive call stack could go as deep as the height of the tree in an unbalanced tree, making it \(O(H)\). For a balanced tree, the space complexity would be \(O(\log N)\), but for a skewed tree, it would be \(O(N)\).
201+
202+
**Optimizations:** This recursive solution is already optimal for this problem. For trees that are extremely skewed, an iterative solution using parent pointers could reduce stack usage, but it generally won’t improve the time complexity.

0 commit comments

Comments
 (0)