Searching & Sorting · 4 of 36
Square root of an integer
Find the biggest square of tiles you can build from a pile of x tiles, by guessing the side length higher or lower.
- Simple way
- time O(sqrt(x))
room O(1) - Clever way
- time O(log x)
room O(1)
The problem, simply
You get a whole number x that is 0 or more. Return its square root rounded down: the biggest whole number r with r * r <= x. You may not use Math.sqrt or a power function.
For example the square root of 8 is about 2.83, so the answer is 2.
A story to remember it
Picture it
Possible sides for x = 20, with the tiles each one needs. Sides up to 4 fit (16 tiles). From side 5 on, it needs more than 20.
- done
- answer
- rejected
Examples
Not a perfect square
- In
x = 8- Out
2
2 x 2 = 4 fits, 3 x 3 = 9 is too big.
A perfect square
- In
x = 16- Out
4
4 x 4 is exactly 16.
Zero and one
- In
x = 0, x = 1- Out
0, 1
Both are their own square roots. Handle them before the search.
The biggest int
- In
x = 2147483647- Out
46340
46340 x 46340 = 2147395600 fits, 46341 x 46341 does not. Squaring a guess here overflows int, so use long.
Way 1: the simple way
Try sides 1, 2, 3, ... while the square still fits. The last side that fits is the answer.
class Solution {
public int mySqrt(int x) {
long r = 0;
while ((r + 1) * (r + 1) <= x) r++; // the next side still fits
return (int) r;
}
}How long it takes: about sqrt(x) steps, O(sqrt(x)). For the biggest int that is 46340 steps, which is fine once, but slow if asked millions of times. Extra room: O(1).
Way 2: the clever way
Binary search the side.
- If
x < 2, returnx. - The answer is between 1 and
x / 2, because forx >= 4the square root is at most half ofx(and for 2 and 3 the answer is 1). - Take
mid. Ifmid * mid <= x, it fits: remember it and look for a bigger side (lo = mid + 1). Otherwise it is too big (hi = mid - 1). - The last side that fit is the answer.
Higher or lower on the side length
01 / 10
x = 20. The side is somewhere from 1 to 20 / 2 = 10.
- x
- 20
- checking
- done
- answer
- rejected
class Solution {
public int mySqrt(int x) {
if (x < 2) return x;
long lo = 1, hi = x / 2, answer = 1;
while (lo <= hi) {
long mid = lo + (hi - lo) / 2;
if (mid * mid <= x) { // long math, so no overflow
answer = mid; // fits: try a bigger side
lo = mid + 1;
} else {
hi = mid - 1; // too many tiles: smaller side
}
}
return (int) answer;
}
}How long it takes: the range halves each round, O(log x), about 31 rounds at most. Extra room: O(1).
Another way: Newton's method
Start with a guess r = x. Replace it by the average of r and x / r while r * r > x. If r is too big, x / r is too small, and their average is closer. The number of correct digits roughly doubles each round.
class Solution {
public int mySqrt(int x) {
long r = x;
while (r * r > x) {
r = (r + x / r) / 2;
}
return (int) r;
}
}This is also O(log x) in the worst case, and much faster once it gets close. It is what calculators and libraries do, in spirit.
Watch out for
- Returning
loorhiat the end without thinking. With this loop,hiends as the answer too, but keepingansweris clearer. hi = x / 2is wrong forx = 1(it gives 0). That is whyx < 2is handled first.- Using
Math.sqrtand casting is often allowed in real code, but floating point can be off by one for hugelongvalues. Fix with a smallwhilecheck.
What interviewers ask next
- Return the square root with 6 digits after the point. Binary search on doubles for about 60 rounds, or use Newton's method.
- Is
xa perfect square? Find the floor square root and checkr * r == x. - The cube root, or the k-th root. Same search, with
mid^k <= x, careful with overflow. - Why not search
[0, x]? You can. It costs one extra round.x / 2is just a tighter start.
Remember this
Grown-up words: the side length is the integer square root (floor of the square root), "higher or lower" is binary search on the answer, and averaging r with x / r is Newton's method.