The night-before sheet: find the yes/no check, pick first or last, write your Style A.
๐ญ Your own rules: DSA thoughts โ Binary Search, Must Remember. Each section below links the ones that apply.
โ๏ธ The code follows your Style A from BS #1, but it isn't your own LeetCode code yet. As you solve each problem, send your solution and it replaces the one here.
"Guess the middle. The answer tells you which half to throw away."
Binary search is not really about sorted arrays. It works whenever you can ask a yes/no question about a position (or a value) and the answer flips only once: all โ then all โ, or all โ then all โ.
low and high. 2. What's the check? Write cond(mid) and make sure it flips only once. 3. First โ or last โ? That picks the template.| The problem saysโฆ | Pattern | Picture it as | LC |
|---|---|---|---|
| sorted array, find x exactly | Exact | ๐ guess the number: higher or lower? | 704, 374, 702 |
| first / last position, insert position, first โฅ x, count < x | Boundary | ๐ง a row of โ then โ: find the wall | 35, 34, 278, 744, 2529 |
| peak, mountain array | Boundary on the slope | โฐ๏ธ walk uphill, you always reach a top | 162, 852, 1095 |
| sorted, then rotated | Rotated | ๐ a cut deck: two sorted runs | 153, 33, 81 |
| minimum speed / capacity / days so that it still works | On the answer, first โ | ๐๏ธ a dial: turn it down until it breaks | 875, 1011, 410, 1482 |
| maximize the minimum (gap, candies, removals) | On the answer, last โ | ๐ stretch the gap until it no longer fits | 1552, 2226, 1898 |
| k-th smallest pair distance / matrix value / fraction | On the answer + count | ๐ข "how many are โค mid?" | 378, 719, 786 |
| sorted matrix | 2D | ๐งฎ one long sorted list, or a staircase | 74, 240, 1351 |
| median of two sorted arrays | Partition | โ๏ธ one cut through both arrays | 4 |
| reach / connect using only edges โค mid | On the answer + BFS | ๐ธ๏ธ filter the graph, then ask "reachable?" | 3419, 778 |
๐งญ Size hint: answers up to 10โน and an O(n) check โ about 30 checks, O(n log 10โน). That's usually what the constraints are pointing at.
"Exact: low <= high. First that works: high = mid. Last that works: low = mid, round up."
high = n โ 1.๐ก while(low <= high), and mid ยฑ 1 on both sides because mid was already checked.
// ๐ BINARY SEARCH (exact match)
function search(nums: number[], target: number): number {
let low = 0;
let high = nums.length - 1; // the answer must be inside, so n - 1
while(low <= high) {
const mid = (low + high) >> 1;
if(nums[mid] === target) return mid;
else if(nums[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
};
| # | Problem | The trick |
|---|---|---|
| 374 | Guess Number Higher or Lower | Same code: guess(mid) is โ1 / 1 / 0 instead of comparing. |
| 702 | Search in a Sorted Array of Unknown Size (free text on Taro) | Double high (1, 2, 4, โฆ) until reader.get(high) โฅ target, then exact search. |
๐ก First index with nums[i] โฅ target. If every value is smaller, low lands on the extra slot n, which is exactly the insert position.
// ๐ง SEARCH INSERT POSITION (LEFT: first index with nums[i] >= target)
function searchInsert(nums: number[], target: number): number {
let low = 0;
let high = nums.length; // extra slot: bigger than everything โ n
while(low < high) {
const mid = (low + high) >> 1; // round DOWN
if(nums[mid] >= target) high = mid; // works โ answer is mid or left of it
else low = mid + 1;
}
return low; // low === high === the answer
};
| step | low | high | mid | nums[mid] โฅ 5? ([1,3,5,6]) | move |
|---|---|---|---|---|---|
| 1 | 0 | 4 | 2 | 5 โ | high = 2 |
| 2 | 0 | 2 | 1 | 3 โ | low = 2 |
| 3 | 2 | 2 | low === high | return 2 |
๐ก Last x with x * x โค n. low = mid, so mid must round up: with two left, round down picks low again and the loop never ends.
// ๐ง SQRT(x) (RIGHT: last x with x * x <= n)
function mySqrt(x: number): number {
let low = 0; // 0 * 0 <= x always works, so no -1 slot needed
let high = x;
while(low < high) {
// round UP. x can be 2ยณยน โ 1, so low + high + 1 passes 2ยณยน and ">> 1" turns negative
const mid = Math.floor((low + high + 1) / 2);
if(mid * mid <= x) low = mid; // works โ answer is mid or right of it
else high = mid - 1;
}
return low;
};
high = n โ 1 or high = max(piles). Same reason exact search uses n โ 1.Style B (ans = mid inside the loop, low <= high) gives the same answers. Your BS #1 keeps it only for reading other people's code; write Style A.
"Not 'where is x?' but 'where does โ turn into โ?'"
๐ก One LEFT search (first โฅ target) and one RIGHT search (last โค target). After the first one, check you really found the target.
// ๐ง FIRST AND LAST POSITION (one LEFT search + one RIGHT search)
function searchRange(nums: number[], target: number): number[] {
const n = nums.length;
// first index with nums[i] >= target
let low = 0;
let high = n;
while(low < high) {
const mid = (low + high) >> 1;
if(nums[mid] >= target) high = mid;
else low = mid + 1;
}
if(low === n || nums[low] !== target) return [-1, -1]; // landed on the extra slot, or not the target
const first = low;
// last index with nums[i] <= target
low = -1;
high = n - 1;
while(low < high) {
const mid = (low + high + 1) >> 1; // round UP
if(nums[mid] <= target) low = mid;
else high = mid - 1;
}
return [first, low];
};
๐ก โ = "the next value is smaller" (going downhill). Find the first โ. Compare with mid + 1: round-down mid is always < high, so it exists.
// โฐ๏ธ FIND PEAK ELEMENT (walk uphill)
function findPeakElement(nums: number[]): number {
let low = 0;
let high = nums.length - 1; // a peak always exists, so no extra slot
while(low < high) {
const mid = (low + high) >> 1; // mid < high, so nums[mid + 1] exists
if(nums[mid] > nums[mid + 1]) high = mid; // going down โ a peak is at mid or left
else low = mid + 1; // going up โ a peak is to the right
}
return low;
};
| # | Problem | The trick |
|---|---|---|
| 278 | First Bad Version | First โ with isBadVersion(mid); low = 1, high = n (a bad version exists). |
| 744 | Smallest Letter Greater Than Target | First letter > target; on the extra slot n, wrap to letters[0]. |
| 2529 | Max Count of Positive and Negative | negatives = first index โฅ 0, positives = n โ first index > 0. Two LEFT searches. |
| 1539 | Kth Missing Positive Number | Missing before i = arr[i] โ (i + 1). First i with missing โฅ k โ answer i + k. |
| 1060๐ | Missing Element in Sorted Array | Same idea: missing before i = nums[i] โ nums[0] โ i. |
| 540 | Single Element in a Sorted Array | Pairs sit at (even, odd). First i with nums[i] !== nums[i ^ 1] is the single one. |
| 658 | Find K Closest Elements | Search the window start in 0 โฆ n โ k: first start with x โ arr[mid] โค arr[mid + k] โ x. |
| 2300 | Successful Pairs of Spells and Potions | Sort potions; per spell, first potion with spell ร p โฅ success; count = m โ index. |
| 528 | Random Pick with Weight | Prefix sums; first prefix > random ร total. |
| 852 | Peak Index in a Mountain Array | Exactly 162. |
| 300 / 354 | LIS (tails) / Russian Doll Envelopes | First tail โฅ x: replace it, or push if low lands on the extra slot. |
tails[mid] >= x; your worked example is 354."A rotated array is two sorted runs. Compare with the last value to know which run you're in."
๐ก First index with nums[i] โค nums[n โ 1]. Compare with the last value, not the first: an unrotated array would fool nums[0].
// ๐ FIND MINIMUM IN ROTATED SORTED ARRAY (LEFT: first value <= the last value)
function findMin(nums: number[]): number {
const n = nums.length;
let low = 0;
let high = n - 1; // the minimum always exists
while(low < high) {
const mid = (low + high) >> 1;
if(nums[mid] <= nums[n - 1]) high = mid; // in the low run โ min is mid or left
else low = mid + 1; // in the high run โ min is to the right
}
return nums[low];
};
๐ก Around mid, one half is always sorted. If the target is inside that sorted half, go there; otherwise go to the other half.
// ๐ SEARCH IN ROTATED SORTED ARRAY (exact match + "which half is sorted?")
function search(nums: number[], target: number): number {
let low = 0;
let high = nums.length - 1;
while(low <= high) {
const mid = (low + high) >> 1;
if(nums[mid] === target) return mid;
if(nums[low] <= nums[mid]) { // left half is sorted
if(nums[low] <= target && target < nums[mid]) high = mid - 1;
else low = mid + 1;
} else { // right half is sorted
if(nums[mid] < target && target <= nums[high]) low = mid + 1;
else high = mid - 1;
}
}
return -1;
};
| # | Problem | The trick |
|---|---|---|
| 81 | Search in Rotated Array II (duplicates) | When nums[low] === nums[mid] === nums[high] you can't tell the sorted half: low++, high--. Worst case O(n). |
| 154 | Find Minimum II (duplicates) | When nums[mid] === nums[high], just high--. Worst case O(n). |
| 1095 | Find in Mountain Array | Find the peak (162), then a LEFT search on the way up, then on the way down with the check flipped. |
"Don't search the array. Guess the answer, check it in O(n), keep the half that can still win."
๐ก Hours at speed k = ฮฃ ceil(pile / k). Range 1 โฆ max(pile); the max always works, so no extra slot.
// ๐๏ธ KOKO EATING BANANAS (LEFT on the answer: slowest speed that works)
function minEatingSpeed(piles: number[], h: number): number {
function canFinish(speed: number): boolean {
let hours = 0;
for(const p of piles) hours += Math.ceil(p / speed);
return hours <= h;
}
let low = 1;
let high = Math.max(...piles); // the biggest pile per hour always works, so no extra slot
while(low < high) {
const mid = (low + high) >> 1;
if(canFinish(mid)) high = mid; // works โ try slower
else low = mid + 1;
}
return low;
};
| step | low | high | mid | hours โค 8? | move |
|---|---|---|---|---|---|
| 1 | 1 | 11 | 6 | 6 โ | high = 6 |
| 2 | 1 | 6 | 3 | 10 โ | low = 4 |
| 3 | 4 | 6 | 5 | 8 โ | high = 5 |
| 4 | 4 | 5 | 4 | 8 โ | high = 4 โ return 4 |
๐ก Same shape as 875. Pick low and high as the smallest and largest answers that could ever be right: low = max(weights), high = sum(weights).
// ๐๏ธ CAPACITY TO SHIP PACKAGES WITHIN D DAYS (same shape as 875)
function shipWithinDays(weights: number[], days: number): number {
function canShip(cap: number): boolean {
let need = 1;
let load = 0;
for(const w of weights) {
if(load + w > cap) { // doesn't fit โ start a new day
need++;
load = 0;
}
load += w;
}
return need <= days;
}
let low = Math.max(...weights); // must fit the heaviest package
let high = weights.reduce((a, b) => a + b, 0); // one day ships everything
while(low < high) {
const mid = (low + high) >> 1;
if(canShip(mid)) high = mid;
else low = mid + 1;
}
return low;
};
๐ก "Maximize the minimum gap" โ last gap that still fits m balls. RIGHT search, round up.
// ๐ MAGNETIC FORCE BETWEEN TWO BALLS (RIGHT on the answer: biggest gap that works)
function maxDistance(position: number[], m: number): number {
position.sort((a, b) => a - b);
const n = position.length;
function canPlace(gap: number): boolean {
let balls = 1;
let last = position[0];
for(let i = 1; i < n; i++) {
if(position[i] - last >= gap) {
balls++;
last = position[i];
}
}
return balls >= m;
}
let low = 1; // positions are distinct, so gap 1 always works
let high = position[n - 1] - position[0];
while(low < high) {
const mid = (low + high + 1) >> 1; // round UP
if(canPlace(mid)) low = mid; // works โ try a bigger gap
else high = mid - 1;
}
return low;
};
| # | Problem | First / last โ | The check |
|---|---|---|---|
| 410 | Split Array Largest Sum | ๐๏ธ first | Same as 1011: greedy pieces with sum โค mid, count โค k. Range max โฆ sum. |
| 1482 | Min Days to Make m Bouquets | ๐๏ธ first | Count runs of k bloomed flowers โฅ m. If m ร k > n, return โ1 first. |
| 1283 | Smallest Divisor Given a Threshold | ๐๏ธ first | ฮฃ ceil(x / d) โค threshold. Koko again. |
| 2187 | Minimum Time to Complete Trips | ๐๏ธ first | ฮฃ floor(t / time[i]) โฅ totalTrips; high = min(time) ร totalTrips. |
| 2226 | Maximum Candies Allocated to K Children | ๐ last | ฮฃ floor(c / x) โฅ k. Answer can be 0, so low = 0 is the extra slot (round up keeps mid โฅ 1). |
| 1898 | Maximum Number of Removable Characters | ๐ last | Remove the first mid indices; is p still a subsequence of s? (two pointers) |
| 69 / 367 | Sqrt(x) / Valid Perfect Square | ๐ last | Last x with x ร x โค n; 367 checks x ร x === n. |
| 719 | K-th Smallest Pair Distance | ๐ข first | Sort; count pairs with diff โค mid (sliding window) โฅ k. |
| 1044 | Longest Duplicate Substring | ๐ last | Does some substring of length mid appear twice? (rolling hash) |
| 774๐ | Minimize Max Distance to Gas Station | ๐๏ธ first | Real numbers: loop ~100 times or while(high โ low > 1e-6); stations needed = ฮฃ ceil(gap / d) โ 1 โค k. |
| 3419 / 778 | Min Max Edge Weight / Swim in Rising Water | ๐๏ธ first | Keep edges (or cells) โค mid, BFS: can you reach everything? |
"Fully sorted โ flatten it. Rows and columns sorted โ staircase from the top-right."
๐ก Exact search on 0 โฆ mยทn โ 1, with row = floor(mid / n), col = mid % n (n = number of columns).
// ๐งฎ SEARCH A 2D MATRIX (one sorted list, read row by row)
function searchMatrix(matrix: number[][], target: number): boolean {
const m = matrix.length;
const n = matrix[0].length;
let low = 0;
let high = m * n - 1;
while(low <= high) {
const mid = (low + high) >> 1;
const val = matrix[Math.floor(mid / n)][mid % n]; // index โ (row, col)
if(val === target) return true;
else if(val < target) low = mid + 1;
else high = mid - 1;
}
return false;
};
๐ก Start top-right. Too big โ move left (that column is out). Too small โ move down (that row is out).
// ๐งฎ SEARCH A 2D MATRIX II (staircase from the top-right, O(m + n))
function searchMatrix(matrix: number[][], target: number): boolean {
let row = 0;
let col = matrix[0].length - 1;
while(row < matrix.length && col >= 0) {
if(matrix[row][col] === target) return true;
else if(matrix[row][col] > target) col--; // too big โ this whole column is out
else row++; // too small โ this whole row is out
}
return false;
};
| # | Problem | The trick |
|---|---|---|
| 1351 | Count Negative Numbers in a Sorted Matrix | Staircase from the top-right (or a LEFT search per row for the first negative). |
| 378 | Kth Smallest Element in a Sorted Matrix | ๐ข Search the value: count cells โค mid with the staircase; first mid with count โฅ k. |
| 1428๐ | Leftmost Column with at Least a One | Staircase from the top-right: 1 โ move left, 0 โ move down. |
| 1901 | Find a Peak Element II | Search the rows: take the max of the mid row, compare with the cell below, keep the uphill half. |
"Binary search where to cut the shorter array; the cut in the other array follows."
half โ i from the other, so you only search i.๐ก Use ยฑInfinity for an empty side of a cut. l1 > r2 โ took too many from nums1.
// โ๏ธ MEDIAN OF TWO SORTED ARRAYS (binary search the cut in the shorter array)
function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
if(nums1.length > nums2.length) return findMedianSortedArrays(nums2, nums1);
const m = nums1.length;
const n = nums2.length;
const half = (m + n + 1) >> 1; // size of the left side
let low = 0;
let high = m; // take 0..m values from nums1
while(low <= high) {
const i = (low + high) >> 1; // values taken from nums1
const j = half - i; // the rest come from nums2
const l1 = i === 0 ? -Infinity : nums1[i - 1];
const r1 = i === m ? Infinity : nums1[i];
const l2 = j === 0 ? -Infinity : nums2[j - 1];
const r2 = j === n ? Infinity : nums2[j];
if(l1 <= r2 && l2 <= r1) { // every left value <= every right value
if((m + n) % 2) return Math.max(l1, l2);
return (Math.max(l1, l2) + Math.min(r1, r2)) / 2;
}
if(l1 > r2) high = i - 1; // took too many from nums1
else low = i + 1; // took too few
}
return -1;
};
| # | Problem | The trick |
|---|---|---|
| 786 | K-th Smallest Prime Fraction | ๐ข Search the value v in (0, 1): count fractions โค v with two pointers, stop when the count is k. (A heap works too.) |
If you can say the trick for each in one sentence, you're ready.
| # | Problem | Pattern | The trick |
|---|---|---|---|
| 704 | Binary Search | ๐ Exact | low <= high, high = n โ 1, mid ยฑ 1. |
| 35 | Search Insert Position | ๐ง First โ | First โฅ target; high = n is the insert-at-end slot. |
| 34 | First and Last Position | ๐ง First + last | Two searches; check the first one found the target. |
| 278 | First Bad Version | ๐ง First โ | First isBadVersion. |
| 69 | Sqrt(x) | ๐ Last โ | Last x with xยฒ โค n; round up. |
| 162 | Find Peak Element | โฐ๏ธ Slope | Next is bigger โ go right. |
| 153 | Find Minimum in Rotated | ๐ Rotated | First value โค the last value. |
| 33 | Search in Rotated | ๐ Rotated | One half is sorted: is the target in it? |
| 875 | Koko Eating Bananas | ๐๏ธ First โ | Slowest speed with ฮฃ ceil โค h. |
| 1011 | Ship Within D Days | ๐๏ธ First โ | low = max weight, high = sum; greedy days. |
| 410 | Split Array Largest Sum | ๐๏ธ First โ | 1011 with k pieces. |
| 1552 | Magnetic Force | ๐ Last โ | Biggest gap that fits m balls. |
| 378 | Kth Smallest in Sorted Matrix | ๐ข Count | Count โค mid with the staircase. |
| 74 | Search a 2D Matrix | ๐งฎ Flatten | mid โ (mid / n, mid % n). |
| 240 | Search a 2D Matrix II | ๐งฎ Staircase | Top-right: too big โ, too small โ. |
| 4 | Median of Two Sorted Arrays | โ๏ธ Partition | Search the cut in the shorter array. |
| Bug | Input that exposes it |
|---|---|
Last-that-works search with round down (low = mid) | [5,7], last โค 7 โ low = 0 and mid = 0 forever: infinite loop |
high = n โ 1 when the answer may not exist | 35: [1,3], target 5 โ returns 1, should be 2 |
(low + high + 1) >> 1 on huge values | 69: x = 2147483647 โ (x + 1) >> 1 is โ1073741824 and the answer comes out 0. >> works on 32-bit ints, so use Math.floor(โฆ / 2) once low + high can reach 2ยณยน |
33: nums[low] < nums[mid] instead of <= | [3,1], target 1 โ โ1. When low === mid the left half is one value, and it counts as sorted |
153: comparing with nums[0] instead of the last value | [1,2,3] (not rotated) โ returns 3 |
1011: low = 1 instead of max(weights) | [10], days 2 โ returns 1; a 10 never fits in capacity 1 |
875: Math.floor instead of Math.ceil for hours | [3], h = 1 โ returns 2; at speed 2 the pile takes 2 hours |
| 34: no "is it really the target?" check | [5,7], target 6 โ [1, 0] instead of [โ1, โ1] |
162: comparing with nums[mid โ 1] | [1,2] โ 0. At mid = 0, nums[โ1] is undefined and the compare is just false |
arr[-1] is undefined, so an index bug gives a wrong answer, not a crash.| Pattern | Time | Space |
|---|---|---|
| Exact, boundary, peak, rotated | O(log n) | O(1) |
| Rotated with duplicates (81, 154) | O(n) worst case ([1,1,1,โฆ]) | O(1) |
| On the answer | O(n ยท log range) | O(1) besides the check |
| 74 flatten / 240 staircase | O(log(mยทn)) / O(m + n) | O(1) |
| 378, 719 (count โค mid) | O(n ยท log range) (+ sort for 719) | O(1) |
| 4 median | O(log min(m, n)) | O(1) |
Why log: each step halves the range, and 2ยณโฐ โ 10โน, so even a range of a billion takes about 30 steps.
Cover the right column (it's blurred; hover or tap to reveal). Say the answer out loud first.
| Prompt | Answer |
|---|---|
| Exact search: loop condition and high? | low <= high, high = n โ 1. |
| First that works: where's the extra slot, which way does mid round? | high = n (right side), round down, high = mid. |
| Last that works: where's the extra slot, which way does mid round? | low = โ1 (left side), round up, low = mid. |
| Why must "last that works" round up? | With two left, round down gives mid = low; low = mid changes nothing โ infinite loop. |
| What does landing on the extra slot mean? | Nothing works: no answer (or insert at the end). |
| When can you skip the extra slot? | When an answer always exists (peak, rotated minimum, Koko's max speed). |
| 162: compare mid with which neighbour, and why is it safe? | mid + 1. Round-down mid < high, so it exists. |
| 153: compare with which value? | The last one. โค last โ low run โ go left. |
| How do you spot "binary search on the answer"? | "Minimum X such thatโฆ" or "maximize the minimum", and if X works then every bigger (or smaller) X works too. |
| 1011: low and high? | max(weights) and sum(weights). |
| Maximize the minimum gap: first or last โ? | Last โ, RIGHT search, round up. |
| Sorted matrix, rows and columns only: where do you start? | Top-right (or bottom-left). Each step deletes a row or a column: O(m + n). |
When does >> 1 break? | When low + high reaches 2ยณยน (it works on 32-bit ints). Use Math.floor(โฆ / 2). |
The code on this page is written in your Style A (BS #1) and tested on the LeetCode examples plus random inputs checked against brute force. It isn't your own LeetCode code yet; it gets swapped for yours as you solve each problem.