|
| 1 | +<h1 align='center'>Lowest - Common - Ancestor - Of a - Binary - Search - Tree</h1> |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +**Problem URL :** [Lowest Common Ancestor Of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +## Problem Explanation |
| 11 | +Given a binary search tree (BST) and two nodes `p` and `q`, the task is to find their lowest common ancestor (LCA). The LCA is the lowest node in the tree that has both `p` and `q` as descendants. The definition also implies that both `p` and `q` will be present in the tree. |
| 12 | + |
| 13 | +- **Constraints:** |
| 14 | + - Each node has a unique integer value. |
| 15 | + - The tree does not contain duplicate values. |
| 16 | + - Both `p` and `q` will exist in the tree. |
| 17 | + |
| 18 | +**Expected Output Format:** |
| 19 | +The output should be the node that represents the LCA of `p` and `q`. |
| 20 | + |
| 21 | +**Real-World Analogy:** |
| 22 | +Think of the BST as an organizational chart. The LCA can be seen as the first common manager above two employees in this hierarchy. |
| 23 | + |
| 24 | +**Edge Cases:** |
| 25 | +1. If either `p` or `q` is the root of the tree, then the root is the LCA. |
| 26 | +2. If `p` and `q` are the same node, the LCA is that node itself. |
| 27 | + |
| 28 | +#### Step 2: Approach |
| 29 | + |
| 30 | +**High-Level Overview:** |
| 31 | +The approach leverages the properties of a binary search tree, where the left subtree contains values less than the root, and the right subtree contains values greater than the root. This allows us to traverse the tree efficiently to find the LCA. |
| 32 | + |
| 33 | +**Step-by-Step Breakdown:** |
| 34 | +1. Start at the root of the tree. |
| 35 | +2. Compare the values of the root, `p`, and `q`. |
| 36 | +3. If both values are greater than the root's value, move to the right child (since both nodes must be in the right subtree). |
| 37 | +4. If both values are less than the root's value, move to the left child (since both nodes must be in the left subtree). |
| 38 | +5. If one value is on the left and the other on the right (or one is equal to the root), then the root is the LCA. |
| 39 | + |
| 40 | +**Visual Aids:** |
| 41 | +A diagram could represent a BST, highlighting the path taken based on comparisons with `p` and `q`. |
| 42 | + |
| 43 | +**Pseudocode:** |
| 44 | +```plaintext |
| 45 | +function lowestCommonAncestor(root, p, q): |
| 46 | + if root is NULL: |
| 47 | + return NULL |
| 48 | +
|
| 49 | + if root.val < p.val and root.val < q.val: |
| 50 | + return lowestCommonAncestor(root.right, p, q) |
| 51 | + |
| 52 | + if root.val > p.val and root.val > q.val: |
| 53 | + return lowestCommonAncestor(root.left, p, q) |
| 54 | + |
| 55 | + return root |
| 56 | +``` |
| 57 | + |
| 58 | +## Problem Solution |
| 59 | +```cpp |
| 60 | +/** |
| 61 | + * Definition for a binary tree node. |
| 62 | + * struct TreeNode { |
| 63 | + * int val; |
| 64 | + * TreeNode *left; |
| 65 | + * TreeNode *right; |
| 66 | + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} |
| 67 | + * }; |
| 68 | + */ |
| 69 | + |
| 70 | +class Solution { |
| 71 | +public: |
| 72 | + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { |
| 73 | + if(root == NULL) return NULL; |
| 74 | + |
| 75 | + if(root -> val < p -> val && root -> val < q -> val) { |
| 76 | + return lowestCommonAncestor(root -> right, p, q); |
| 77 | + } |
| 78 | + |
| 79 | + if(root -> val > p -> val && root -> val > q -> val){ |
| 80 | + return lowestCommonAncestor(root -> left, p, q); |
| 81 | + } |
| 82 | + |
| 83 | + return root; |
| 84 | + } |
| 85 | +}; |
| 86 | +``` |
| 87 | + |
| 88 | +## Problem Solution Explanation |
| 89 | +```cpp |
| 90 | +class Solution { |
| 91 | +public: |
| 92 | + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { |
| 93 | + if(root == NULL) return NULL; // If the root is NULL, return NULL |
| 94 | + |
| 95 | + // If both p and q are greater than root, then LCA lies in the right subtree |
| 96 | + if(root -> val < p -> val && root -> val < q -> val) { |
| 97 | + return lowestCommonAncestor(root -> right, p, q); |
| 98 | + } |
| 99 | + |
| 100 | + // If both p and q are less than root, then LCA lies in the left subtree |
| 101 | + if(root -> val > p -> val && root -> val > q -> val){ |
| 102 | + return lowestCommonAncestor(root -> left, p, q); |
| 103 | + } |
| 104 | + |
| 105 | + // We have found the split point, i.e., either p or q is on one side and the other is on the other side |
| 106 | + return root; // This root is the LCA of p and q |
| 107 | + } |
| 108 | +}; |
| 109 | +``` |
| 110 | + |
| 111 | +**Detailed Code Walkthrough:** |
| 112 | +- **Line 1-2:** Defines the `Solution` class and its member function `lowestCommonAncestor`, which takes the root of the BST and two nodes, `p` and `q`. |
| 113 | +- **Line 3:** Checks if the `root` is `NULL`. If it is, it means we've reached the end of a path without finding the LCA, so return `NULL`. |
| 114 | +- **Line 5:** If both `p` and `q` have values greater than `root->val`, it means both nodes are located in the right subtree. So we make a recursive call on the right child. |
| 115 | +- **Line 8:** Similarly, if both nodes have values less than `root->val`, they are in the left subtree, and we make a recursive call on the left child. |
| 116 | +- **Line 11:** If neither of the above conditions holds true, it indicates that we've found the point of divergence, meaning `root` is the LCA of `p` and `q`, and we return it. |
| 117 | + |
| 118 | +#### Step 4: Output Examples |
| 119 | + |
| 120 | +**Example 1:** |
| 121 | +- **Input:** |
| 122 | + ``` |
| 123 | + 6 |
| 124 | + / \ |
| 125 | + 2 8 |
| 126 | + / \ / \ |
| 127 | + 0 4 7 9 |
| 128 | + / \ |
| 129 | + 3 5 |
| 130 | + ``` |
| 131 | + Let `p = 2` and `q = 8`. |
| 132 | +- **Output:** `6` |
| 133 | +- **Explanation:** The root node `6` is the LCA since `2` is in the left subtree and `8` is in the right subtree. |
| 134 | + |
| 135 | +**Example 2:** |
| 136 | +- **Input:** |
| 137 | + ``` |
| 138 | + 6 |
| 139 | + / \ |
| 140 | + 2 8 |
| 141 | + / \ / \ |
| 142 | + 0 4 7 9 |
| 143 | + / \ |
| 144 | + 3 5 |
| 145 | + ``` |
| 146 | + Let `p = 2` and `q = 4`. |
| 147 | +- **Output:** `2` |
| 148 | +- **Explanation:** The node `2` is the LCA since `4` is in its right subtree. |
| 149 | + |
| 150 | +**Example 3:** |
| 151 | +- **Input:** |
| 152 | + ``` |
| 153 | + 6 |
| 154 | + / \ |
| 155 | + 2 8 |
| 156 | + / \ / \ |
| 157 | + 0 4 7 9 |
| 158 | + / \ |
| 159 | + 3 5 |
| 160 | + ``` |
| 161 | + Let `p = 3` and `q = 5`. |
| 162 | +- **Output:** `4` |
| 163 | +- **Explanation:** The node `4` is the LCA since `3` is in its left subtree and `5` is in its right subtree. |
| 164 | + |
| 165 | +#### Step 5: Time and Space Complexity |
| 166 | + |
| 167 | +**Time Complexity:** |
| 168 | +- The time complexity of the solution is **O(h)**, where **h** is the height of the tree. In the worst case (for a skewed tree), this could be **O(n)**, where **n** is the number of nodes. |
| 169 | + |
| 170 | +**Space Complexity:** |
| 171 | +- The space complexity is also **O(h)** due to the recursion stack. In the worst case, this can also be **O(n)** for a skewed tree, but for a balanced tree, it will be **O(log n)**. |
| 172 | + |
| 173 | +**Optimization Opportunities:** |
| 174 | +- Since the algorithm is already efficient with a time complexity of O(h), there's limited scope for further optimization. However, if you need to optimize for space, you could consider an iterative approach that avoids recursion, although this would typically involve using a stack or queue to maintain state. |
| 175 | + |
| 176 | +### Additional Considerations |
| 177 | +- **Target Audience:** Ensure explanations are accessible to beginners by avoiding overly technical jargon and providing clear examples. |
| 178 | +- **Interactive Learning:** Recommend platforms like LeetCode or HackerRank where users can practice similar problems interactively. |
| 179 | +- **Error Handling:** While not specifically applicable in this context, always consider edge cases and validate inputs when implementing similar solutions. |
| 180 | +- **Best Practices:** Use meaningful variable names, comment code effectively, and structure code to enhance readability and maintainability. |
0 commit comments