Skip to content

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 mid and long 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

  • 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!