56. Sqrt(x)
๐ LeetCode Problem: 69. Sqrt(x)
๐ Difficulty: Easy
๐ท๏ธ Topics: Math, Binary Search
Problem Statement
Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well.
You must not use any built-in exponent function or operator.
- For example, do not use
pow(x, 0.5)in C++ orx ** 0.5in Python.
Example 1:
Input: x = 4
Output: 2
Explanation: The square root of 4 is 2, so we return 2.
Example 2:
Input: x = 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since we round it down to the nearest integer, 2 is returned.
Constraints:
- 0 <= x <= 2^31 - 1
๐จ Visual Understanding
The Problem Visualized
Find: floor(โx)
x = 8
โ8 = 2.82842...
โ (round down)
Answer: 2
Visual representation:
0 1 2 3 4 5 6 7 8 9
โ
Answer: 2
2ยฒ = 4 โค 8 โ
3ยฒ = 9 > 8 โ
So 2 is the largest integer whose square โค 8
x = 16
โ16 = 4.0 (exact)
Answer: 4
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
โ
Answer: 4
4ยฒ = 16 โค 16 โ
5ยฒ = 25 > 16 โ
๐จ CRITICAL INSIGHT - Why Binary Search Works
The Key Pattern!
We're searching for the largest integer k such that kยฒ โค x
Think of it as searching in this sequence:
Numbers: 0 1 2 3 4 5 6 7 8 9 10
Squares: 0 1 4 9 16 25 36 49 64 81 100
For x = 8:
Which numbers have square โค 8?
0ยฒ = 0 โค 8 โ
1ยฒ = 1 โค 8 โ
2ยฒ = 4 โค 8 โ
3ยฒ = 9 > 8 โ
4ยฒ = 16 > 8 โ
...
Pattern: [โ โ โ | โ โ โ ...]
[YYYYYYYY | NNNNNN ...]
We need the LAST โ (largest valid)
= Find rightmost position where square โค x
= Binary Search on Answer Space!
Why This is Binary Search on Answer Space
Search Space:
Not searching in an array
Searching range [0, x] for answer
Each "candidate" is a potential sqrt
Each candidate has a property: square โค x or square > x
The Goal:
Find LARGEST number whose square โค x
= Find rightmost valid position
= "Find Last/Upper Bound" pattern!
Key Insight:
If kยฒ โค x, then answer is k or larger
If kยฒ > x, then answer is smaller than k
Can eliminate half at each step!
Example: x = 8, range [0, 8]
Check mid = 4:
4ยฒ = 16 > 8
Answer must be < 4
Search [0, 3]
Check mid = 1:
1ยฒ = 1 โค 8
Answer could be 1 or larger
Search [2, 3]
Check mid = 2:
2ยฒ = 4 โค 8
Answer could be 2 or larger
Search [3, 3]
Check mid = 3:
3ยฒ = 9 > 8
Answer must be < 3
Search ends
Best found: 2 โ
The Binary Search Invariant
Throughout the search:
All numbers with square โค x are in [left, result]
All numbers with square > x are in [result+1, right]
We track the best valid answer found so far:
When midยฒ โค x: update result = mid, search right
When midยฒ > x: search left
After loop: result = largest number whose square โค x
๐ฏ Approach 1: Linear Search (Brute Force)
The Most Natural Thinking ๐ญ
Core Logic:
Start from 0 and check each number:
If iยฒ โค x and (i+1)ยฒ > x, then i is the answer
Or simply: find largest i where iยฒ โค x
Implementation
public int mySqrt(int x) {
if (x < 2) return x; // 0 and 1 are special cases
// Check each number from 0 to x
for (int i = 0; i <= x; i++) {
long square = (long) i * i; // Use long to avoid overflow
// If current square exceeds x, previous was answer
if (square > x) {
return i - 1;
}
// If exact match
if (square == x) {
return i;
}
}
return x; // Should never reach here
}
โฐ Time: O(โx) - Check up to โx numbers
๐พ Space: O(1) - Only variables
Why This Works:
We increment i until iยฒ > x
Then i-1 is the largest number whose square โค x
โ Problem: Too slow for large x! If x = 2^31-1, checking billions of numbers!
๐ฏ Approach 2: Binary Search (Optimal) โญ
Better Approach ๐ก
๐ง The Core Insight
The answer is in range [0, x]
We're looking for: largest k where kยฒ โค x
This is MONOTONIC:
If kยฒ โค x, then (k-1)ยฒ also โค x
If kยฒ > x, then (k+1)ยฒ also > x
Pattern: [valid valid valid ... valid | invalid invalid ...]
[YYYYYYYYYYYYYYYYYYYY | NNNNNNNNNNNNN]
Find the LAST valid (rightmost Y)
= Binary Search on Answer Space!
The Strategy:
Binary search on range [0, x]:
If midยฒ โค x:
mid is valid, might be answer
But there might be larger valid number
Track mid as potential answer
Search right half
If midยฒ > x:
mid is too large
Search left half
After loop: return best valid number found
Implementation
public int mySqrt(int x) {
// Edge cases
if (x < 2) return x; // sqrt(0) = 0, sqrt(1) = 1
int left = 0;
int right = x;
int result = 0; // Track best valid answer
while (left <= right) {
// Safe mid calculation
int mid = left + (right - left) / 2;
// Use long to avoid overflow when calculating mid * mid
long square = (long) mid * mid;
if (square == x) {
// Exact square root found
return mid;
}
if (square < x) {
// mid is valid, but there might be larger valid number
result = mid; // Track this as potential answer
left = mid + 1; // Search for larger
} else {
// mid is too large
right = mid - 1; // Search for smaller
}
}
return result; // Best valid answer found
}
โฐ Time: O(log x) - Binary search
๐พ Space: O(1) - Only variables
๐ Dry Run
Example 1: x = 8
Goal: Find largest k where kยฒ โค 8
Initial: left = 0, right = 8, result = 0
[0, 1, 2, 3, 4, 5, 6, 7, 8]
โ โ
left right
Iteration 1:
mid = 0 + (8 - 0) / 2 = 4
square = 4 ร 4 = 16
16 > 8, too large
right = 3
[0, 1, 2, 3, 4, 5, 6, 7, 8]
โ โ
left right
Iteration 2:
mid = 0 + (3 - 0) / 2 = 1
square = 1 ร 1 = 1
1 โค 8, valid!
result = 1 (track it)
left = 2
[0, 1, 2, 3, 4, 5, 6, 7, 8]
โ โ
left right
Iteration 3:
mid = 2 + (3 - 2) / 2 = 2
square = 2 ร 2 = 4
4 โค 8, valid!
result = 2 (better answer)
left = 3
[0, 1, 2, 3, 4, 5, 6, 7, 8]
โ
left/right
Iteration 4:
mid = 3 + (3 - 3) / 2 = 3
square = 3 ร 3 = 9
9 > 8, too large
right = 2
[0, 1, 2, 3, 4, 5, 6, 7, 8]
โ โ
right left
Loop ends (left > right)
return result = 2 โ
Verification: 2ยฒ = 4 โค 8, 3ยฒ = 9 > 8 โ
Example 2: x = 4
Goal: Find largest k where kยฒ โค 4
Initial: left = 0, right = 4, result = 0
[0, 1, 2, 3, 4]
โ โ
left right
Iteration 1:
mid = 0 + (4 - 0) / 2 = 2
square = 2 ร 2 = 4
4 == 4, exact match!
return 2 โ
Verification: โ4 = 2 exactly โ
Example 3: x = 0
Edge case: x < 2
return x = 0 โ
Example 4: x = 1
Edge case: x < 2
return x = 1 โ
Example 5: x = 2147395599 (large number)
Initial: left = 0, right = 2147395599
After ~31 binary search iterations:
Finds answer = 46339
Verification: 46339ยฒ = 2147395521 โค 2147395599 โ
46340ยฒ = 2147488400 > 2147395599 โ
Without long cast:
46340 ร 46340 would overflow!
That's why we use (long) mid * mid
๐ฏ Why This Works - Deep Dive
The Search Space:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Range: [0, x]
But can we optimize?
For x > 1, โx < x/2
So we can search [0, x/2]... but x/2 still large
For safety and simplicity, search [0, x]
Binary search is fast enough!
The result Variable:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Why track result separately?
When midยฒ โค x, mid is VALID
But there might be LARGER valid number
So we:
1. Save mid as potential answer (result = mid)
2. Keep searching right (left = mid + 1)
If we find larger valid, we update result
If all larger numbers are invalid, result has answer!
This is "Find Last Valid" pattern:
Keep tracking best valid found
Keep searching for better
Overflow Prevention:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Critical: long square = (long) mid * mid
Why?
mid can be up to x โ 2^31
mid ร mid can overflow int range
Example:
mid = 46341
mid ร mid = 2,147,488,281
As int: OVERFLOW (wraps to negative!)
As long: Correct positive value โ
Always cast to long BEFORE multiplication!
Why Not Check mid > x/mid?
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Alternative to avoid overflow:
Instead of: mid ร mid > x
Check: mid > x / mid
This works but:
- Division is slower than multiplication
- Needs special handling when mid = 0
- Less clear code
Using long cast is cleaner and fast enough!
The "Find Last Valid" Pattern
This is the template for finding LAST/LARGEST valid:
1. Track result variable (best found so far)
2. When condition is TRUE:
result = mid (track it)
left = mid + 1 (search for better)
3. When condition is FALSE:
right = mid - 1 (search smaller)
4. Return: result after loop
Applies to:
- Find largest number with property
- Find rightmost valid position
- Find floor/maximum under constraint
Key insight:
result preserves best answer found
Continue searching for better
โ ๏ธ Common Mistakes to Avoid
Mistake 1: Not using long for square
// โ WRONG - Integer overflow
int square = mid * mid;
if (square > x) { ... }
// โ CORRECT - Use long
long square = (long) mid * mid;
if (square > x) { ... }
Why it's critical:
For x = 2147395599, mid = 46341
mid * mid as int: OVERFLOW (negative number!)
Comparison breaks completely!
Cast to long BEFORE multiplication:
(long) mid * mid = correct positive value โ
Mistake 2: Not tracking result
// โ WRONG - Might return wrong answer
while (left <= right) {
if (mid * mid <= x) {
left = mid + 1; // Lost the valid mid!
}
}
return left; // Wrong!
// โ CORRECT - Track result
int result = 0;
while (left <= right) {
if (square <= x) {
result = mid; // Save it!
left = mid + 1;
}
}
return result; // Correct
Mistake 3: Wrong edge case handling
// โ WRONG - Fails for x = 0 or x = 1
int left = 1; // Should be 0
int right = x;
// โ CORRECT - Handle 0 and 1
if (x < 2) return x;
int left = 0;
Mistake 4: Using float/double
// โ WRONG - Problem says not to use pow or exponents
return (int) Math.sqrt(x); // Not allowed!
return (int) Math.pow(x, 0.5); // Not allowed!
// โ CORRECT - Binary search
// (Our solution)
Mistake 5: Setting right = x for large x
// โ ๏ธ INEFFICIENT but works
int right = x; // For x = 2^31, unnecessary large range
// โ BETTER - Optimize right bound
int right = Math.min(x, 46341); // โ(2^31) โ 46340
// But for simplicity, right = x is fine
// Binary search is fast anyway!
๐ฏ Pattern Recognition
Problem Type: Binary Search - Mathematical/On Answer Space
Core Pattern: Find Largest Value with Constraint
When to Apply:
โ Mathematical computation without library
โ Looking for LARGEST/MAXIMUM satisfying condition
โ Monotonic property: if k works, all < k work
โ Can check if value is valid
โ Need O(log n) time
Recognition Keywords:
- "Square root"
- "Rounded down"
- "Must not use built-in"
- Find maximum/largest
- Mathematical constraint
Similar Problems:
- Valid Perfect Square (LC 367)
- Pow(x, n) (LC 50)
- Divide Two Integers (LC 29)
- Guess Number Higher or Lower (LC 374)
- First Bad Version (LC 278)
Key Difference from Other BS:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Find First/Minimum: โ
โ - Search for smallest valid โ
โ - right = mid when valid โ
โ โ
โ Find Last/Maximum (This Problem): โ
โ - Search for largest valid โ
โ - result = mid when valid โ
โ - left = mid + 1 to find larger โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Track best answer and keep searching!
๐ง Interview Strategy
Step 1: "Need to find largest k where kยฒ โค x"
Step 2: "This is binary search on answer space [0, x]"
Step 3: "Pattern: [valid valid ... | invalid invalid ...]"
Step 4: "Track best valid, keep searching for larger"
Step 5: "Use long for overflow prevention"
Key Points to Mention:
- Binary search on RANGE, not array
- Looking for LARGEST valid number
- Track result as best valid found
- Continue searching for larger valid
- Cast to long prevents overflow
- Handle edge cases x < 2
- Time: O(log x), Space: O(1)
- Can't use Math.sqrt() or pow()
Why This Approach?
"I need to find the largest integer whose square is at most x.
This is perfect for binary search on the answer space [0, x].
The property is monotonic - if k is too large, all larger values
are also too large. I'll track the best valid answer found so far
and keep searching for a larger one. The critical detail is using
long when calculating mid squared to avoid integer overflow for
large values of x."
Overflow Handling:
"When mid is large, mid ร mid can overflow integer range. For
example, 46341 squared is over 2 billion. So I cast to long
before multiplication to handle this safely."
๐ Quick Revision Notes
๐ฏ Core Concept:
Find Square Root (floor): Find largest k where kยฒ โค x using binary search on range [0, x]. Track best valid answer. When valid: save and search right. When invalid: search left. Critical: Use long for square calculation!
โก Quick Implementation:
class Test {
public int mySqrt(int x) {
int left = 0; // since in question, 0 is also in range of inputs
int right = x;
int res = 0;
while (left <= right) {
int mid = left + (right - left) / 2;
long product = (long) mid * mid;
if(product == x) {
return mid;
} else if(product < x) {
// mid is valid, but there might be larger valid number
// hence could be a potential answer
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
public static void main(String[] args) {
Test t = new Test();
for(int i = 0; i < 17; i++) {
System.out.println("for i = "+i+": "+t.mySqrt(i));
}
}
}
๐ Key Insights:
- Pattern: Binary Search on Answer Space / Find Maximum
- Goal: Largest k where kยฒ โค x
- Monotonic: If kยฒ > x, then (k+1)ยฒ also > x
- Track Result: Save best valid answer
- Search Right: When valid, look for larger
- Search Left: When invalid, look for smaller
- Overflow: Use
(long) mid * midโ CRITICAL! - Edge Cases: x < 2 handled separately
- Loop:
left <= right(standard BS) - Time: O(log x), Space: O(1)
๐ช Memory Aid:
"Find the LAST valid, track the BEST! Cast to LONG to avoid the test!"
Think: "Largest kยฒ โค x = Track best valid!"
๐ก The Key Insight:
When midยฒ โค x:
result = mid (save it!)
left = mid + 1 (find larger!)
When midยฒ > x:
right = mid - 1 (too big!)
When midยฒ == x:
return mid (exact!)
โ ๏ธ Critical Details:
1. Edge case: if (x < 2) return x
2. Overflow: long square = (long) mid * mid
3. Track: result = mid when valid
4. Search: left = mid + 1 when valid
5. Loop: while (left <= right)
6. Return: result (best valid found)
๐ฅ The Overflow Issue:
For large x near 2^31:
mid can be ~46340
mid ร mid as int: OVERFLOW!
mid ร mid as long: Correct โ
Example:
mid = 46341
As int: mid * mid = negative (overflow!)
As long: (long) mid * mid = 2,147,488,281 โ
Always: (long) mid * mid
Never: mid * mid
๐ฏ The Pattern - Find Last Valid:
Search space: [0, x]
Property: kยฒ โค x
[0, 1, 2, ..., k, k+1, ..., x]
[โ โ โ ... โ โ ... โ]
โ
Find this!
When โ: save it, search right (maybe larger โ)
When โ: search left (need smaller)
After loop: result = rightmost โ
๐ก Why Track Result:
Without tracking:
Find valid mid = 2
Search right for larger
Find invalid mid = 3
Search left
Loop ends
Lost the answer 2!
With tracking:
Find valid mid = 2
result = 2 (saved!)
Search right
Find invalid mid = 3
Search left
Loop ends
Return result = 2 โ
๐งช Edge Cases
Case 1: x = 0
Input: 0
Output: 0
โ0 = 0
Case 2: x = 1
Input: 1
Output: 1
โ1 = 1
Case 3: Perfect square
Input: x = 16
Output: 4
โ16 = 4 exactly
Case 4: Not perfect square
Input: x = 8
Output: 2
โ8 โ 2.828, floor = 2
Case 5: Large number
Input: x = 2147395599
Output: 46339
Without long cast: OVERFLOW!
With long cast: Correct โ
Case 6: x = 2
Input: 2
Output: 1
โ2 โ 1.414, floor = 1
All handled correctly! โ
Related Patterns
- Valid Perfect Square (LC 367) - Similar but checks exact match
- Pow(x, n) (LC 50) - Binary search for exponentiation
- Divide Two Integers (LC 29) - Binary search for division
- First Bad Version (LC 278) - Binary search on range
Happy practicing! ๐ฏ
Note: This is a classic "binary search on answer space" problem! The overflow prevention with long cast is the critical detail that many candidates miss!