skip to content

How does a row-by-row column-height reduction turn a binary seat grid into one histogram scan per row?

level: seniorimportance: nice to knowfreq 32%

answer

  1. Turn two dimensions into many one-dimensional passes
  2. Each row gets its own baseline
  3. A column remembers unbroken free depth
  4. An occupied cell erases that memory
  5. Every rectangle has exactly one bottom row

basics

~20 s

Sweep rows top to bottom keeping, per column, the count of consecutive free cells ending at the current row; an occupied cell resets that column to zero. Each updated row is a histogram, scanned for its widest block.

solid answer

~50 s

Maintain one array `heights` with a slot per column. Moving down the grid, set `heights[c] = heights[c] + 1` if the seat at the current row and column `c` is free, and `heights[c] = 0` if it is occupied. After updating a row, `heights` is exactly a silhouette of free depth ending at that row, so a single widest-block scan over it gives the widest all-free block whose **bottom edge lies on this row**. Every rectangle in the grid has some bottom row, so taking the maximum across rows covers all of them with no double counting. The reset is the correctness hinge: it is what encodes "free all the way up". Total cost is one increment plus one linear scan per row, so it is proportional to the number of cells rather than to anything quadratic in the rows.

go deeper

for a junior

Know the shape of the trick: a two-dimensional block question can become a stack of one-dimensional ones by remembering, per column, how many free cells stack up above the current row.

for a middle

Explain the two hinges — the reset at an occupied cell, which makes a height mean unbroken freedom, and the carry across rows, which keeps the sweep proportional to the number of cells.

for a senior

Argue completeness out loud: every rectangle has exactly one bottom row, so maximising over rows misses nothing. Then name what the reduction does not answer — non-rectangular regions and real seating constraints.

for a principal

Own the recompute-versus-incremental call for a live booking surface, and push back when the elegant reduction answers a different question than the product asked. Correct shape assumptions matter more than the scan's constant factor.

## The setting A venue-booking view is a grid of seats, each free or taken, and the feature has to find the largest solid rectangular block of free seats — the biggest group that can be seated together. Naively this is a two-dimensional search: choose a top row, a bottom row, a left column and a right column, then verify. That is hopeless. The reduction turns it into a sequence of one-dimensional problems you already know how to solve. ## The reduction Keep one array `heights`, one slot per column, and sweep rows top to bottom: ``` for each row r: for each column c: if seat[r][c] is free: heights[c] = heights[c] + 1 else: heights[c] = 0 best = max(best, widest_block(heights)) ``` After processing row `r`, `heights[c]` is the number of consecutive free seats in column `c` ending at row `r`. Read the array as a silhouette of bars and it *is* a histogram — one whose baseline is row `r`. The widest solid block under that silhouette is precisely the widest all-free block whose bottom edge lies on row `r`. ## Why the maximum over rows is complete Every rectangle has exactly one bottom row. When the sweep reaches that row, the rectangle's columns all have height at least the rectangle's own height, so the histogram scan of that row can see it. Taking the maximum over all rows therefore considers every candidate at least once, and never invents a rectangle that does not exist — because a height of `k` in column `c` is a *proof* of `k` consecutive free seats there. Completeness and soundness both fall out of the definition of `heights`, which is the argument to give out loud. ## The reset is the whole correctness argument The single line that matters is `heights[c] = 0` at an occupied seat. Carry the height through a taken seat and the histogram now claims free depth across a booked seat; the algorithm will happily report a block straddling somebody's reservation. The reset is what makes a height mean *unbroken* freedom upward, and it is the first thing to check when the output looks too good. ## Cost, and the trap of recomputing Carrying `heights` across rows makes each row cost one pass to update plus one linear histogram scan — proportional to the number of columns. Over the whole grid that is proportional to the number of cells, which for venue-sized grids is nothing. The trap is recomputing each column's height from scratch by scanning upward from every cell. That is proportional to the number of rows per cell, multiplying the total by the row count for no benefit. The incremental carry is not a micro-optimisation; it is what keeps the reduction linear in the grid. ## What it does and does not buy The result is the largest **axis-aligned, contiguous** block. It is not the largest connected free region of arbitrary shape, and it is not the best seating arrangement under real constraints — aisles, price tiers, a group that will accept two adjacent rows. If the product needs those, the histogram reduction is the wrong tool and saying so is worth more than the clever code. That judgement — recognising when an elegant reduction answers a subtly different question than the one the product asked — is what the scenario is really testing. ## Updates in a live system Bookings arrive continuously. A single seat flipping to occupied changes `heights` only in that column, from that row downward until the next occupied seat — but the largest block can move anywhere, so no cheap local patch to the *answer* exists. In practice the whole sweep is re-run: for a venue-shaped grid it is microseconds, so recompute-on-change is the right default, and incremental cleverness here buys complexity rather than latency. If the grid grew to something where that stops being true, the honest move is to bound the query (largest block within a section, or largest block of at most a given size) rather than to preserve an exact global answer incrementally. ## Interview framing The expected answer is short: *maintain per-column free depth, reset at occupied cells, run the one-dimensional widest-block scan once per row, take the maximum.* Then volunteer the reset as the correctness hinge and the carry as the cost hinge. Candidates who have only memorised the one-dimensional scan can usually produce the sweep, but stumble when asked why the maximum over bottom rows misses nothing.

  • What goes wrong if you do not reset a column at an occupied cell?
    The height stops meaning "unbroken free depth" and becomes a running total, so the histogram claims free space across a booked seat. The scan then reports blocks that straddle occupied cells — an answer that is too large and geometrically impossible. The reset is what encodes free-all-the-way-up.
  • Why carry the heights row to row instead of recomputing them per row?
    Recomputing means scanning upward from every cell, which multiplies the total work by the number of rows. The carry updates each column in constant time per row, keeping the sweep proportional to the number of cells. It is the difference between linear in the grid and linear in the grid times its height.
  • One seat gets booked — how much has to be recomputed?
    Heights change only in that column from that row down to the next occupied seat, but the largest block can shift anywhere, so the answer itself has no cheap local patch. For venue-sized grids re-running the whole sweep is microseconds, so recompute-on-change is the right default; incremental structures here buy complexity, not latency.
  • Does this find the largest free region of any shape?
    No. It finds the largest axis-aligned contiguous rectangle only. An L-shaped or diagonal free region is invisible to it, as are product constraints like aisles or price tiers. If the requirement is really "largest group that can sit together", confirm the shape assumption before shipping the reduction.

saying these in an interview costs you the question

  • Resets every column at the start of each row
  • Carries height through an occupied cell
  • Claims the answer is just the tallest column
  • Recomputes each column by scanning upward per cell
  • Reports it as the largest free region of any shape

context