69. Sqrt(x)

Compute the integer square root of x — the floor of √x — without a built-in sqrt. Because mid·mid increases monotonically, you can binary search the answer in [0, x] in O(log x).

EasyBinary SearchMathTypeScript

PROBLEM What we're solving

Given a non-negative integer x, return floor(√x)— the largest integer whose square does not exceed x— without any built-in sqrt or power operator. For example, x = 8 2, because 2·2 = 4 ≤ 8 but 3·3 = 9 > 8. And x = 164 (exact square).

KEY IDEA The answer space is monotone

Insight → the predicate "is mid·mid ≤ x?" is monotone: it is true for every small mid and flips to false once mid grows past √x. That monotone true→false boundary is exactly what binary search finds. Search candidate answers in [0, x], and keep the largest mid whose square still fits.

RECIPE Binary search on the answer

  • 0 · Handle tiny x. For x < 2 (i.e. 0 or 1), floor(√x) = x. Return early so mid = 0 never appears in the overflow-safe division.
  • 1 · Set the answer range. lo = 1, hi = x, ans = 1. The true answer lies somewhere in this inclusive window.
  • 2 · Probe the midpoint. mid = lo + Math.floor((hi - lo) / 2)— the overflow-safe midpoint.
  • 3 · Compare without overflow. Test mid·mid ≤ x as mid <= x / mid so the product never overflows. If it holds, mid is a valid floor: record ans = mid and search higher with lo = mid + 1. Otherwise mid is too big → hi = mid - 1.
  • 4 · Return the best. When the window collapses, the last recorded ans is the largest mid with mid·mid ≤ x — that is floor(√x).
Classic confusion → why not just check mid * mid ≤ x directly? In fixed-width languages (Java/C++) mid * mid can overflow for large x and silently wrap negative, breaking the comparison. Writing it as mid <= x / mid (or using a 64-bit / BigInt product) keeps every value in range. In JS the number is a 64-bit float, but the idiom is the canonical, language-portable form.

COST Complexity & alternatives

Linear scan upward
O(√x)
Increment i until i·i > x. Simple, but slow for large x.
Binary search the answer
O(log x)
O(log x) time; O(1) space.

Space note

Only a few integer variables are needed → O(1)space. Newton's method (r = (r + x / r) / 2 until it stabilises) also runs in roughly O(log x) iterations and O(1) space, but binary search is easier to reason about and to get exactly right.

Pattern transfer →"binary search on the answer" is the real lesson here: instead of searching an array, you search a range of candidate values and test a monotone feasibility predicate. The same shape solves Koko Eating Bananas (LC 875), Capacity to Ship Packages (LC 1011), Split Array Largest Sum (LC 410), and Find the Smallest Divisor (LC 1283).

RUN IT Binary search the answer in [0, x]

step 0 / 7
STARTSearch the answer in [1, 8]. lo=1, hi=8, ans=1. Keep the largest mid with mid·mid ≤ 8.
1function mySqrt(x: number): number {
2 if (x < 2) return x; // 0 -> 0, 1 -> 1
3
4 let lo = 1;
5 let hi = x;
6 let ans = 1;
7
8 while (lo <= hi) {
9 const mid = lo + Math.floor((hi - lo) / 2);
10 if (mid <= x / mid) { // mid*mid <= x, overflow-safe
11 ans = mid; // mid is a valid floor candidate
12 lo = mid + 1; // try for something bigger
13 } else {
14 hi = mid - 1; // mid too big, shrink
15 }
16 }
17 return ans; // largest mid with mid*mid <= x
18}
candidates (val / val²):112439416525636749864
State
1
lo
8
hi
mid
mid*mid
1
ans
candidate window [lo, hi]mid (current probe)ans (best floor so far)window exhausted
slowfast

TYPESCRIPT The solution, annotated

mySqrt.ts
function mySqrt(x: number): number {
  if (x < 2) return x;            // 0 -> 0, 1 -> 1

  let lo = 1;
  let hi = x;
  let ans = 1;

  while (lo <= hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    if (mid <= x / mid) {          // mid*mid <= x, overflow-safe
      ans = mid;                   // mid is a valid floor candidate
      lo = mid + 1;                // try for something bigger
    } else {
      hi = mid - 1;                // mid too big, shrink
    }
  }
  return ans;                      // largest mid with mid*mid <= x
}

Reading it block by block

Line 2 — handle 0 and 1. For x < 2 the floor of the root is x itself. Returning early also guarantees mid ≥ 1 later, so the overflow-safe x / mid never divides by zero.
Lines 4–6 — set the answer range. The root lies in [1, x] for x ≥ 2. ans starts at 1 (always valid since 1·1 = 1 ≤ x) and tracks the best floor found so far.
Lines 8–9 — inclusive loop + safe midpoint. lo <= hi keeps a single-element window in play. mid = lo + Math.floor((hi - lo) / 2) is the overflow-safe midpoint.
Lines 10–13 — the monotone test. mid <= x / mid is mid·mid ≤ x written to avoid overflow. When it holds, mid is a feasible floor: save it to ans and push lo right to hunt for something larger.
Line 15 — too big. If mid·mid > x, every value ≥ mid is also too big, so discard them with hi = mid - 1.
Line 18 — return the best floor. When lo > hi the window is empty; ans holds the largest mid with mid·mid ≤ x, which is exactly floor(√x).
Complexity → O(log x) time — the candidate range [1, x] halves each iteration. O(1) extra space — only a handful of integer variables.

INTERVIEWFollow-ups they'll ask

  • "Why mid <= x / mid not mid * mid <= x?" The product can overflow a 32/64-bit integer for large x; the division keeps operands in range. Alternatively cast to a wider type or use BigInt.
  • "Return the ceiling instead?" Take the floor result r; if r·r !== x, the ceiling is r + 1.
  • "Compute the root to a few decimals?" Continue binary search on a real-valued range with an epsilon stop, or switch to Newton's method which converges quadratically.
  • "Newton's method?" Iterate r = (r + x / r) / 2 from a guess until it stops decreasing — also O(log x), O(1), but trickier to bound exactly.
  • "What's the brute force?" Increment i from 0 while i·i ≤ x; the last such i is the answer — O(√x).

OPTIMAL Binary Search

mySqrt.ts
function mySqrt(x: number): number {
  if (x < 2) return x;            // 0 -> 0, 1 -> 1

  let lo = 1;
  let hi = x;
  let ans = 1;

  while (lo <= hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    if (mid <= x / mid) {          // mid*mid <= x, overflow-safe
      ans = mid;                   // mid is a valid floor candidate
      lo = mid + 1;                // try for something bigger
    } else {
      hi = mid - 1;                // mid too big, shrink
    }
  }
  return ans;                      // largest mid with mid*mid <= x
}
Complexity → O(log x) time — the candidate range [1, x] halves each iteration. O(1) extra space — only a handful of integer variables.

ALT 1 Brute force — scan upward

O(√x) time · O(1) space

Walk i upward while i·i ≤ x; the last such i is the floor of the root.

approach-2.ts
function mySqrt(x: number): number {
  let i = 0;
  while (i <= x / Math.max(i, 1) && i * i <= x) i++;
  return i - 1 < 0 ? 0 : i - 1;
}
Note → Correct and trivially simple, but it takes √x steps. Binary searching the same range reaches the boundary in O(log x).

ALT 2 Newton's method

O(log x) time · O(1) space

Iterate r = (r + x / r) / 2 from an over-estimate; it converges quadratically to √x, then floor it.

approach-3.ts
function mySqrt(x: number): number {
  if (x < 2) return x;
  let r = x;
  while (r > x / r) {
    r = Math.floor((r + Math.floor(x / r)) / 2);
  }
  return r;
}
Note → Fewer iterations in practice and elegant, but the convergence and termination conditions are easy to get subtly wrong under interview pressure. Binary search is the safer default.

MNEMONIC The one-liner

"Binary search [1, x]; keep the biggest mid whose square fits; compare with mid <= x/mid."

TRIGGERS When you see ___ → reach for ___

integer sqrt / floor of a rootbinary search the answer in [0, x]
monotone "is value feasible?" predicatebinary search on the answer space
risk of mid*mid overflowcompare as mid <= x / mid (or BigInt)
"largest value satisfying P"record candidate, then lo = mid + 1

SKELETON The reusable shape

skeleton.ts
function mySqrt(x: number): number {
  if (x < 2) return x;
  let lo = 1, hi = x, ans = 1;
  while (lo <= hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    if (mid <= x / mid) { ans = mid; lo = mid + 1; }
    else                  hi = mid - 1;
  }
  return ans;
}

FLASHCARDS Tap to flip

What makes binary search applicable to Sqrt(x)?
The predicate mid·mid ≤ x is monotone (true for small mid, false past √x), so the boundary can be found by halving the candidate range.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 9
QUESTION 01
For x = 8, what does mySqrt return?
QUESTION 02
What is the time complexity of the binary-search solution?
QUESTION 03
Why test mid <= x / mid rather than mid * mid <= x?
QUESTION 04
When mid·mid ≤ x holds, what does the algorithm do?
QUESTION 05
Why is the special case if (x < 2) return x; included?
QUESTION 06
For x = 16, what is returned?
QUESTION 07
This problem is a canonical example of which broader technique?
QUESTION 08
#69 · Sqrt(x)Compute the integer square root of x without a sqrt call by binary searching the answer in [0, x], keeping the largest mid whose square does not exceed x (using mid ≤ x/mid to dodge overflow). O(log x).Which algorithmic approach does this primarily use?
QUESTION 09
#69 · Sqrt(x)Compute the integer square root of x without a sqrt call by binary searching the answer in [0, x], keeping the largest mid whose square does not exceed x (using mid ≤ x/mid to dodge overflow). O(log x).Which implementation correctly solves it?