</> MAANG.io
coding interview · 301

Advanced

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/95 solved 0% complete

Search the Answer

What is this?

None of these three problems hands you a sorted array with a target in it. What each does have is a quantity where the answer is a boundary: below some side length every square fits, above it none do; below some start position the window is improvable, above it it is not; below some cumulative sum the draw belongs to an earlier index.

Recognising the boundary is the work. The loop that finds it is the same five lines every time.

flowchart TD A["what flips exactly once?"] --> B["square side length
fits ↔ does not fit"] A --> C["window start position
improvable ↔ not"] A --> D["cumulative sum
below the draw ↔ above it"] B --> B1["verify in O(1) with a 2-D prefix sum"] C --> C1["compare arr[mid] and arr[mid+k]"] D --> D1["first prefix strictly greater than the draw"]

💡 Fun fact: Find K Closest Elements is usually attempted by finding the closest value and expanding outward with two pointers — O(log n + k) and perfectly fine. The sharper version binary-searches the window's starting index over the range [0, n − k], comparing x − arr[mid] against arr[mid + k] − x: if the element just past the window is closer than the one at its start, the window must shift right. It searches over positions rather than values, which is the idea worth taking away.

🔓 The 3 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: identify the quantity whose predicate flips once — a size, a position, a running total — and binary-search that. The thing being searched is rarely the thing you were handed.


1. Search a size, verify with a prefix sum

Build the 2-D prefix sum once, padded with a zero row and column:

P = [[0] * (n + 1) for _ in range(m + 1)]
for r in range(m):
    for c in range(n):
        P[r+1][c+1] = mat[r][c] + P[r][c+1] + P[r+1][c] - P[r][c]

def square_sum(r, c, side):                       # top-left (r, c), 0-indexed
    return P[r+side][c+side] - P[r][c+side] - P[r+side][c] + P[r][c]

The - P[r][c] in the build and the + P[r][c] in the query are the same inclusion–exclusion correction: the top-left region is subtracted twice and must be added back once. Padding is what removes every boundary check — without it, r-1 and c-1 need guarding in four places.

With square_sum costing O(1), feasibility for a given side is one sweep of the grid, and the side length is binary-searched over [0, min(m, n)].

2. Search a position

lo, hi = 0, len(arr) - k                          # candidate window starts
while lo < hi:
    mid = (lo + hi) // 2
    if x - arr[mid] > arr[mid + k] - x:           # the far end is closer → shift right
        lo = mid + 1
    else:
        hi = mid
return arr[lo:lo + k]

The comparison is between the element at the window's start and the element just past its end. If dropping the start and gaining the next element is an improvement, the window belongs further right. The > rather than >= is what implements the stated tie-break — on equal distance the smaller value wins, which means keeping the left window.

3. Search a distribution

prefix = list(itertools.accumulate(weights))
target = random.random() * prefix[-1]
return bisect.bisect_right(prefix, target)        # first cumulative sum > target

Each index owns a slice of the line whose length is its weight, so a uniform draw lands in it exactly in proportion. Using bisect_right on a strictly increasing prefix (weights are positive) gives the first index whose cumulative sum exceeds the draw — and drawing from [0, total) rather than (0, total] keeps zero-probability edge cases out.


4. A 30-second worked example (window start)

arr = [1, 2, 3, 4, 5], k = 4, x = 3. Candidate starts are 0 and 1.

lo=0, hi=1        mid = 0
   x - arr[0]     = 3 - 1 = 2
   arr[0+4] - x   = 5 - 3 = 2
   2 > 2 is false → hi = 0
lo == hi == 0     window = arr[0:4] = [1, 2, 3, 4]   ✓

The tie is what makes this instructive: both ends are two away, and the specification prefers the smaller values, so the window stays left. Using >= here would shift it right and return [2,3,4,5] — the wrong answer, and one that passes every non-tied test.


5. Where you'll actually meet this

  • Computer vision. Integral images make region queries O(1), and largest-region-under-threshold is this exact search.
  • Load balancing and sharding. Weighted server selection per request is the cumulative-sum draw.
  • Recommendation and experimentation. Weighted sampling and A/B bucket assignment use the same construction.
  • Search UIs. "K nearest results to this value" backs range and date pickers.
  • Capacity planning. Smallest size that satisfies a measurable-but-not-invertible constraint.

6. Problems in this chapter

▶ Maximum Side Length of a Square with Sum ≤ Threshold

Largest square whose sum stays under a threshold. Binary-search the side length; check feasibility with a padded 2-D prefix sum.
Pattern: search a size + O(1) region verification. Target: O(mn log(min(m,n))) time, O(mn) space.

▶ Find K Closest Elements

The k values closest to x, in ascending order. Binary-search the window's start over [0, n−k], comparing the element at the start against the one just past the end.
Pattern: search a position. Target: O(log(n−k) + k) time, O(1) extra space.

▶ Random Pick with Weight

Draw an index with probability proportional to its weight. Build cumulative sums once, then binary-search a uniform draw.
Pattern: search a distribution. Target: O(n) build, O(log n) per pick.


7. Common pitfalls 🚫

  • Getting the inclusion–exclusion sign wrong. The corner is subtracted twice and must be added back once, in both the build and the query.
  • Not padding the prefix table, which forces boundary checks in four places and invites off-by-one errors.
  • Using >= in the window comparison. The tie-break says prefer the smaller values, which means keeping the left window.
  • Searching over [0, n] for the window start. The range is [0, n − k], or the slice runs off the end.
  • Drawing from (0, total] in the weighted pick, which can exclude the first index when its weight is small.
  • Rebuilding the cumulative sums per draw. Build once in the constructor.
  • bisect_left instead of bisect_right, which mis-assigns draws that land exactly on a boundary.

8. Key takeaways

  1. Binary search needs a predicate that flips once — not an array and not a target.
  2. Search sizes, positions and totals, not only values.
  3. A padded 2-D prefix sum makes any rectangle query O(1) and removes all the boundary cases.
  4. Compare the two ends of a candidate window to decide which way it must move.
  5. Cumulative sums turn a uniform draw into a weighted one — binary search used to generate rather than to find.
  6. Tie-breaks are part of the specification, and here one comparison operator implements the whole rule.
  7. Why interviewers like it: none of these announce themselves as binary search, so spotting the monotonic boundary is the entire signal.

Order: Maximum Side Length of a Square with Sum ≤ Threshold → Find K Closest Elements → Random Pick with Weight.

Random Pick with Weight

Core idea: Lay the weights end-to-end as contiguous segments on a number line. A bigger weight owns a longer segment. Throw a uniform random dart at the line and binary-search for the segment it lands in — longer segments get hit proportionally more often. The segment boundaries are exactly the prefix sums, so bisect_left over them turns "where did the dart land?" into an O(log n) lookup.

The problem (LeetCode 528)

You are given an integer array w where w[i] is the weight of index i. Implement pickIndex() which returns an index i at random with probability w[i] / sum(w).

You will call the constructor once and pickIndex() many times, so we are allowed to do work up front (build a data structure) and want each pick to be fast.

Rephrased as a fresh scenario

Imagine a raffle drum. Each person i buys w[i] tickets and drops them in. You spin the drum and pull one ticket — a person holding more tickets is more likely to be drawn, exactly in proportion to how many tickets they bought. pickIndex() is "pull one ticket and tell me whose it is."

The trick: we never physically store one entry per ticket (that could be millions of entries). We store only the running ticket count after each person — the prefix sums — and binary-search them.

Input / output

w (weights) total A valid pickIndex() return Probability of each index
[1] 1 0 always index 0 → 100%
[1, 3] 4 0 or 1 0 → 25%, 1 → 75%
[2, 5, 3] 10 0, 1, or 2 0 → 20%, 1 → 50%, 2 → 30%

Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.