58. Valid Perfect Square
๐ LeetCode Problem: 367. Valid Perfect Square
๐ Difficulty: Easy
๐ท๏ธ Topics: Math, Binary Search
Problem Statement
Given a positive integer num, return true if num is a perfect square or false otherwise.
A perfect square is an integer that is the square of an integer. In other words, it is the product of some integer with itself.
You must not use any built-in library function, such as sqrt.
Example 1:
Input: num = 16
Output: true
Explanation: We return true because 4 * 4 = 16 and 4 is an integer.
Example 2:
Input: num = 14
Output: false
Explanation: We return false because 3.742 * 3.742 = 14 and 3.742 is not an integer.
Constraints:
- 1 <= num <= 2^31 - 1
๐จ Visual Understanding
The Problem Visualized
Perfect Squares:
1 = 1ยฒ
4 = 2ยฒ
9 = 3ยฒ
16 = 4ยฒ
25 = 5ยฒ
36 = 6ยฒ
...
Non-Perfect Squares:
2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 17, ...
Example: num = 16
Check: Is there an integer k such that kยฒ = 16?
Try k = 1: 1ยฒ = 1 โ 16
Try k = 2: 2ยฒ = 4 โ 16
Try k = 3: 3ยฒ = 9 โ 16
Try k = 4: 4ยฒ = 16 โ MATCH!
Answer: true
Example: num = 14
Check: Is there an integer k such that kยฒ = 14?
Try k = 3: 3ยฒ = 9 < 14
Try k = 4: 4ยฒ = 16 > 14
No exact match between 3 and 4
Answer: false
๐จ CRITICAL INSIGHT - Why Binary Search Works
The Key Pattern!
We need to find if there EXISTS an integer k where kยฒ = num
Think of the search space:
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 num = 16:
0ยฒ = 0 < 16
1ยฒ = 1 < 16
2ยฒ = 4 < 16
3ยฒ = 9 < 16
4ยฒ = 16 = 16 โ FOUND!
5ยฒ = 25 > 16
...
Pattern: [< < < < = > > > ...]
[Too Small | Match | Too Large]
This is SORTED and MONOTONIC!
= Perfect for Binary Search!
Why This is Binary Search on Answer Space
Search Space:
Range [0, num] for potential square root
Each candidate k has a property:
kยฒ < num (too small)
kยฒ = num (perfect match!) โ
kยฒ > num (too large)
The Goal:
Find if ANY k exists where kยฒ = num
= Search for EXACT match
= Classic Binary Search!
Key Insight:
If kยฒ < num, try larger k (search right)
If kยฒ > num, try smaller k (search left)
If kยฒ = num, found it! โ
Can eliminate half at each step!
Example: num = 16, range [0, 16]
Check mid = 8:
8ยฒ = 64 > 16
Too large, search left [0, 7]
Check mid = 3:
3ยฒ = 9 < 16
Too small, search right [4, 7]
Check mid = 5:
5ยฒ = 25 > 16
Too large, search left [4, 4]
Check mid = 4:
4ยฒ = 16 = 16 โ
Perfect square! Return true
Connection to Sqrt(x) Problem
Valid Perfect Square vs Sqrt(x):
Sqrt(x):
Find: floor(โnum)
Return: largest k where kยฒ โค num
Always returns a value
Valid Perfect Square:
Find: does โnum exist as integer?
Check: does kยฒ = num for some k?
Returns: true/false
They're related:
num is perfect square IF floor(โnum)ยฒ = num
But binary search approach differs slightly:
Sqrt: Track best, search for larger
Perfect Square: Search for exact match
๐ฏ Approach 1: Linear Search (Brute Force)
The Most Natural Thinking ๐ญ
Core Logic:
Start from 1 and check each number:
If iยฒ = num, return true
If iยฒ > num, return false (won't find it beyond this)
Implementation
public boolean isPerfectSquare(int num) {
if (num == 1) return true; // 1 is a perfect square
// Check each number starting from 1
for (long i = 1; i <= num; i++) {
long square = i * i;
if (square == num) {
return true; // Found perfect square
}
if (square > num) {
return false; // Exceeded num, won't find it
}
}
return false; // Should never reach here
}
โฐ Time: O(โnum) - Check up to โnum numbers
๐พ Space: O(1) - Only variables
Why This Works:
We check 1ยฒ, 2ยฒ, 3ยฒ, ... until we find match or exceed num
Since squares grow quickly, this is at most โnum iterations
โ Problem: Too slow for large num! If num = 2^31-1, too many checks!
๐ฏ Approach 2: Binary Search (Optimal) โญ
Better Approach ๐ก
๐ง The Core Insight
The search space is SORTED:
1ยฒ < 2ยฒ < 3ยฒ < 4ยฒ < ...
We're looking for EXACT match:
kยฒ = num
This is classic binary search:
If kยฒ < num: search right
If kยฒ > num: search left
If kยฒ = num: found it!
The Strategy:
Binary search on range [1, num]:
Calculate midยฒ:
If midยฒ = num: Perfect square! Return true
If midยฒ < num: Search right half
If midยฒ > num: Search left half
If loop ends without finding: Return false
Implementation
public boolean isPerfectSquare(int num) {
if (num == 1) return true; // Edge case
long left = 1;
long right = num;
while (left <= right) {
long mid = left + (right - left) / 2;
long square = mid * mid; // Use long to avoid overflow
if (square == num) {
return true; // Perfect square found!
}
if (square < num) {
left = mid + 1; // Search right half
} else {
right = mid - 1; // Search left half
}
}
return false; // No perfect square root found
}
โฐ Time: O(log num) - Binary search
๐พ Space: O(1) - Only variables
๐ Dry Run
Example 1: num = 16
Goal: Find if there exists k where kยฒ = 16
Initial: left = 1, right = 16
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
โ โ
left right
Iteration 1:
mid = 1 + (16 - 1) / 2 = 8
square = 8 ร 8 = 64
64 > 16, too large
right = 7
[1, 2, 3, 4, 5, 6, 7, 8, 9, ...]
โ โ
left right
Iteration 2:
mid = 1 + (7 - 1) / 2 = 4
square = 4 ร 4 = 16
16 == 16 โ
return true
Answer: true โ
Example 2: num = 14
Goal: Find if there exists k where kยฒ = 14
Initial: left = 1, right = 14
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
โ โ
left right
Iteration 1:
mid = 1 + (14 - 1) / 2 = 7
square = 7 ร 7 = 49
49 > 14, too large
right = 6
[1, 2, 3, 4, 5, 6, 7, ...]
โ โ
left right
Iteration 2:
mid = 1 + (6 - 1) / 2 = 3
square = 3 ร 3 = 9
9 < 14, too small
left = 4
[1, 2, 3, 4, 5, 6, 7, ...]
โ โ
left right
Iteration 3:
mid = 4 + (6 - 4) / 2 = 5
square = 5 ร 5 = 25
25 > 14, too large
right = 4
[1, 2, 3, 4, 5, 6, 7, ...]
โ
left/right
Iteration 4:
mid = 4 + (4 - 4) / 2 = 4
square = 4 ร 4 = 16
16 > 14, too large
right = 3
[1, 2, 3, 4, 5, 6, 7, ...]
โ โ
right left
Loop ends (left > right)
return false
Answer: false โ
No integer k exists where kยฒ = 14
Example 3: num = 1
Edge case: num == 1
return true โ
1 = 1ยฒ (perfect square)
Example 4: num = 25
Initial: left = 1, right = 25
Iteration 1:
mid = 13
square = 169 > 25
right = 12
Iteration 2:
mid = 6
square = 36 > 25
right = 5
Iteration 3:
mid = 3
square = 9 < 25
left = 4
Iteration 4:
mid = 4
square = 16 < 25
left = 5
Iteration 5:
mid = 5
square = 25 = 25 โ
return true
Answer: true โ
๐ฏ Why This Works - Deep Dive
The Search Space:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Range: [1, num]
Can we optimize?
Yes! For num > 4, โnum < num/2
So we could search [1, num/2]
But optimization is minor:
Binary search on [1, num] is already O(log num)
Binary search on [1, num/2] is O(log num/2) โ O(log num)
For simplicity, use [1, num]
The Three Cases:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1. midยฒ = num: FOUND! Return true immediately
2. midยฒ < num: Answer is to the right (larger k)
3. midยฒ > num: Answer is to the left (smaller k)
Loop Termination:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Standard binary search: left <= right
When loop ends (left > right):
We've checked all candidates
No exact match found
Return false
Unlike Sqrt(x):
Sqrt: Always has answer (floor)
Perfect Square: Might not have answer (false)
Overflow Prevention:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Critical: Use long for mid and square
Why?
num can be up to 2^31 - 1
mid can be up to num
mid ร mid can overflow int range
Example:
num = 2,147,395,600
mid = 46,340
mid ร mid as int: OVERFLOW!
mid ร mid as long: Correct โ
Use long for both mid and square!
Comparison with Sqrt(x)
Sqrt(x) Problem:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Goal: Find floor(โnum)
Return: Integer (always)
Logic: Track best valid, search for larger
Loop: while (left <= right)
When valid: result = mid, left = mid + 1
Return: result
Valid Perfect Square:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Goal: Check if โnum is integer
Return: Boolean (true/false)
Logic: Search for exact match
Loop: while (left <= right)
When match: return true immediately
Return: false if no match
Both use binary search, but:
Sqrt tracks best approximation
Perfect Square searches exact match
โ ๏ธ Common Mistakes to Avoid
Mistake 1: Not using long
// โ WRONG - Integer overflow
int mid = (left + right) / 2;
int square = mid * mid;
if (square == num) { ... }
// โ CORRECT - Use long
long mid = left + (right - left) / 2;
long square = mid * mid;
if (square == num) { ... }
Why it's critical:
For num near 2^31:
mid can be ~46340
mid * mid as int: OVERFLOW (negative!)
Comparison breaks completely!
Use long throughout:
long mid, long square
Prevents all overflow issues โ
Mistake 2: Using Math.sqrt()
// โ WRONG - Problem says not to use library
double root = Math.sqrt(num);
return (int) root * (int) root == num;
// โ CORRECT - Binary search
// (Our solution)
Mistake 3: Wrong range
// โ WRONG - Misses edge case
long left = 2; // Should be 1
long right = num;
// โ CORRECT
long left = 1; // Handles num = 1
Mistake 4: Not handling num = 1
// โ WRONG - Fails for num = 1
long left = 2; // Skips 1!
// โ CORRECT - Special case or start from 1
if (num == 1) return true;
long left = 1;
Mistake 5: Inefficient optimization
// โ ๏ธ UNNECESSARY COMPLEXITY
if (num < 2) return true;
long right = num / 2; // Optimize upper bound
// โ SIMPLER - Binary search is fast anyway
long right = num; // Simple and works
๐ฏ Pattern Recognition
Problem Type: Binary Search - Mathematical/Exact Match
Core Pattern: Search for Exact Value in Monotonic Space
When to Apply:
โ Mathematical computation without library
โ Looking for EXACT match (not approximate)
โ Monotonic property: values increase/decrease
โ Can check if candidate is valid
โ Need O(log n) time
Recognition Keywords:
- "Perfect square"
- "Product of integer with itself"
- "Must not use built-in"
- Boolean return (true/false)
- Exact match required
Similar Problems:
- Sqrt(x) (LC 69) - Related but finds floor
- Pow(x, n) (LC 50) - Binary search for power
- First Bad Version (LC 278) - Find transition
- Guess Number Higher or Lower (LC 374)
Key Difference from Sqrt(x):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sqrt(x): โ
โ - Returns floor(โnum) โ
โ - Always has answer โ
โ - Tracks best approximation โ
โ โ
โ Valid Perfect Square (This Problem): โ
โ - Returns true/false โ
โ - Might not have answer โ
โ - Searches for exact match โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Search for exact, not approximate!
๐ง Interview Strategy
Step 1: "Need to check if num = kยฒ for some integer k"
Step 2: "Binary search on range [1, num]"
Step 3: "Search for EXACT match (not approximate)"
Step 4: "If found, return true. If loop ends, return false"
Step 5: "Use long to prevent overflow"
Key Points to Mention:
- Binary search on RANGE, not array
- Looking for EXACT match
- Three cases: equal, less than, greater than
- Return true immediately when found
- Return false if loop ends (no match)
- Use long for mid and square (overflow)
- Handle edge case num = 1
- Time: O(log num), Space: O(1)
- Can't use Math.sqrt()
Why This Approach?
"I need to check if there's an integer whose square equals num.
The search space [1, num] has the property that squares are
monotonically increasing, making it perfect for binary search.
I'm looking for an exact match, not an approximation. If I find
a candidate where mid squared equals num, I return true immediately.
If the binary search completes without finding a match, I return
false. The critical detail is using long to prevent overflow when
calculating mid squared for large values."
Overflow Handling:
"When num is large, mid can be around 46000. Squaring this overflows
integer range. So I use long for both mid and square to safely handle
values up to 2^31 - 1."
Difference from Sqrt:
"Unlike finding the square root where we track the best approximation,
here we're looking for an EXACT match. Either a perfect square root
exists, or it doesn't. So we can return as soon as we find a match,
or return false if we exhaust the search space."
๐ Quick Revision Notes
๐ฏ Core Concept:
Valid Perfect Square: Check if num = kยฒ for some integer k using binary search on range [1, num]. Search for EXACT match. When match found: return true. When loop ends: return false. Critical: Use long for overflow!
โก Quick Implementation:
class Test {
public boolean isPerfectSquare(int x) {
int left = 0; // since in question, 0 is also in range of inputs
int right = x;
while (left <= right) {
int mid = left + (right - left) / 2;
long product = (long) mid * mid;
if(product == x) {
return true;
} else if(product < x) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.isPerfectSquare(16));
System.out.println(t.isPerfectSquare(14));
}
}
๐ Key Insights:
- Pattern: Binary Search - Exact Match
- Goal: Check if kยฒ = num for some k
- Monotonic: 1ยฒ < 2ยฒ < 3ยฒ < ... (sorted)
- Three Cases: =, <, > (standard BS)
- Return True: When exact match found
- Return False: When loop ends (no match)
- Overflow: Use
long midandlong squareโ CRITICAL! - Edge Case: num = 1 handled separately
- Loop:
left <= right(standard BS) - Time: O(log num), Space: O(1)
๐ช Memory Aid:
"Search for EXACT, use LONG for the test!"
Think: "kยฒ = num? Binary search says YES or NO!"
๐ก The Key Insight:
When midยฒ = num:
return true (found it!)
When midยฒ < num:
left = mid + 1 (search right)
When midยฒ > num:
right = mid - 1 (search left)
Loop ends:
return false (no match)
โ ๏ธ Critical Details:
1. Edge case: if (num == 1) return true
2. Use long: long left, long right, long mid
3. Overflow: long square = mid * mid
4. Exact: if (square == num) return true
5. Loop: while (left <= right)
6. Default: return false after loop
๐ฅ The Overflow Issue:
For num 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: mid * mid = 2,147,488,281 โ
Solution: Use long everywhere!
long left, long right, long mid, long square
๐ฏ Difference from Sqrt(x):
Sqrt(x):
Goal: Find floor(โnum)
Track best valid answer
When valid: result = mid, search right
Return: result (always has value)
Valid Perfect Square:
Goal: Check if โnum is integer
Search for exact match
When match: return true immediately
Return: false if no match
Both: Use binary search, handle overflow
Sqrt: Approximation (floor)
Perfect: Exact match (true/false)
๐ก Quick Check:
Perfect square if: kยฒ = num for integer k
Examples:
16 = 4ยฒ โ true
14 โ kยฒ for any integer k โ false
25 = 5ยฒ โ true
26 โ kยฒ for any integer k โ false
๐งช Edge Cases
Case 1: num = 1
Input: 1
Output: true
1 = 1ยฒ
Case 2: Small perfect square
Input: num = 4
Output: true
4 = 2ยฒ
Case 3: Small non-perfect square
Input: num = 5
Output: false
2ยฒ = 4 < 5, 3ยฒ = 9 > 5
Case 4: Large perfect square
Input: num = 2147395600
Output: true
46340ยฒ = 2147395600
Case 5: Large non-perfect square
Input: num = 2147483647 (2^31 - 1)
Output: false
46340ยฒ < 2147483647 < 46341ยฒ
Case 6: Another perfect square
Input: num = 100
Output: true
10ยฒ = 100
All handled correctly with long! โ
๐ Mathematical Insight
Perfect Squares Pattern:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1, 4, 9, 16, 25, 36, 49, 64, 81, 100, ...
โ โ โ โ โ โ โ โ โ โ
1ยฒ 2ยฒ 3ยฒ 4ยฒ 5ยฒ 6ยฒ 7ยฒ 8ยฒ 9ยฒ 10ยฒ
Gaps between consecutive perfect squares:
4 - 1 = 3
9 - 4 = 5
16 - 9 = 7
25 - 16 = 9
...
Gaps increase by 2 each time!
This is why linear search is O(โn):
Only โn perfect squares โค n
And why binary search is O(log n):
Search space shrinks exponentially
Related Patterns
- Sqrt(x) (LC 69) - Finds floor instead of checking exact
- Pow(x, n) (LC 50) - Binary search for exponentiation
- First Bad Version (LC 278) - Find transition point
- Guess Number Higher or Lower (LC 374) - Similar search
Happy practicing! ๐ฏ
Note: This is a variation of the Sqrt(x) problem, but looking for exact match instead of floor! The overflow prevention is the key detail!