Problem
Given a positive integer, determine whether it is a perfect square — that is, whether some integer multiplied by itself equals it — without using any built-in square root function.
- Input:
num = 16 - Output:
true - Explanation: 4 × 4 = 16, so 16 is a perfect square.
Counter-example: num = 14 → false, because no integer satisfies x² = 14.
Intuition
We need to find whether an integer x exists such that x² = num. The sequence of squares 1, 4, 9, 16, 25, … is strictly increasing, so this is really a search problem on a sorted structure. Binary search can eliminate half the remaining candidates at every step, cutting the O(√n) linear scan down to O(log n).
Approach 1 — Linear Scan
Step through candidate integers starting at 1. If the candidate's square matches num, it is a perfect square. Stop as soon as the square exceeds num.
- Set
candidateto 1. - While
candidate²≤num, check ifcandidate²equalsnum. - If equal, return
true. - Otherwise increment
candidateand repeat. - If the loop exits without a match, return
false.
1def isPerfectSquare(num: int) -> bool:
2 candidate = 1
3 while candidate * candidate <= num:
4 if candidate * candidate == num:
5 return True
6 candidate += 1
7 return FalseTime: O(√n) — we test at most √n candidates before the square exceeds num.
Space: O(1) — only a single counter variable.
Approach 2 — Binary Search
Binary search the range [1, num] for an integer whose square equals num. Because squares are monotonically increasing, a comparison tells us exactly which half of the remaining range to keep.
- Set
left = 1,right = num. - Compute
mid = left + (right - left) / 2to avoid overflow. - Compute
square = mid * mid. - If
square == num, returntrue. - If
square < num, the answer must lie abovemid— setleft = mid + 1. - If
square > num, the answer must lie belowmid— setright = mid - 1. - If the loop ends, no perfect square was found — return
false.
1def isPerfectSquare(num: int) -> bool:
2 left, right = 1, num
3 while left <= right:
4 mid = left + (right - left) // 2
5 square = mid * mid
6 if square == num:
7 return True
8 elif square < num:
9 left = mid + 1 # perfect square root must be larger than mid
10 else:
11 right = mid - 1 # perfect square root must be smaller than mid
12 return FalseTime: O(log n) — the search space halves on every iteration.
Space: O(1) — a constant number of variables.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Linear Scan | O(√n) | O(1) | Simple to implement and reason about; fine for small inputs |
| Binary Search | O(log n) | O(1) | Preferred whenever inputs can be large or O(log n) is required |
Common Mistakes
- Integer overflow when computing
mid * midin Java or C++: fornumnear 2³¹ − 1,midcan be around 46,340, andmid * midoverflows a 32-bit int. Declaremidand the product aslong. - Using
(left + right) / 2instead ofleft + (right - left) / 2: the sumleft + rightcan itself overflow when both values are large; the safer form avoids this. - Condition
left < rightinstead ofleft <= right: when the answer sits at the position whereleft == right, the stricter condition exits the loop one iteration early and incorrectly returnsfalse. - Starting the linear scan at 0:
0 * 0 == 0creates a false positive fornum = 0, and the problem guaranteesnum ≥ 1, so 1 is the correct starting point. - Forgetting that
num = 1is a valid perfect square: 1 = 1², so any logic that starts checking from 2 or uses a strict<comparison in the loop will mishandle this boundary case.
Related Problems
sqrtx— nearly identical binary search, but returns the integer floor of √x rather than a booleanbinary-search— foundational binary search on a sorted arraykoko-eating-bananas— binary search on a monotonic answer spacecapacity-to-ship-packages-within-d-days— binary search on a feasibility functionfind-peak-element— binary search on a structural property rather than an exact target value