Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.
Example 1:
Input: heights = [2,1,5,6,2,3] Output: 10 Explanation: The above is a histogram where width of each bar is 1. The largest rectangle is shown in the red area, which has an area = 10 units.
Example 2:
Input: heights = [2,4] Output: 4
Constraints:
1 <= heights.length <= 1050 <= heights[i] <= 104Problem summary: Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Stack
[2,1,5,6,2,3]
[2,4]
maximal-rectangle)maximum-score-of-a-good-subarray)import java.util.*;
class Solution {
public int largestRectangleArea(int[] heights) {
Deque<Integer> stack = new ArrayDeque<>();
int best = 0;
for (int i = 0; i <= heights.length; i++) {
int h = (i == heights.length) ? 0 : heights[i];
while (!stack.isEmpty() && heights[stack.peek()] > h) {
int height = heights[stack.pop()];
int left = stack.isEmpty() ? -1 : stack.peek();
int width = i - left - 1;
best = Math.max(best, height * width);
}
stack.push(i);
}
return best;
}
}
func largestRectangleArea(heights []int) int {
stack := []int{}
best := 0
for i := 0; i <= len(heights); i++ {
h := 0
if i < len(heights) {
h = heights[i]
}
for len(stack) > 0 && heights[stack[len(stack)-1]] > h {
idx := stack[len(stack)-1]
stack = stack[:len(stack)-1]
left := -1
if len(stack) > 0 {
left = stack[len(stack)-1]
}
width := i - left - 1
area := heights[idx] * width
if area > best {
best = area
}
}
stack = append(stack, i)
}
return best
}
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
stack = []
best = 0
for i in range(len(heights) + 1):
h = 0 if i == len(heights) else heights[i]
while stack and heights[stack[-1]] > h:
idx = stack.pop()
left = stack[-1] if stack else -1
width = i - left - 1
best = max(best, heights[idx] * width)
stack.append(i)
return best
impl Solution {
pub fn largest_rectangle_area(heights: Vec<i32>) -> i32 {
let mut stack: Vec<usize> = Vec::new();
let mut best = 0;
for i in 0..=heights.len() {
let h = if i == heights.len() { 0 } else { heights[i] };
while let Some(&top) = stack.last() {
if heights[top] <= h {
break;
}
let idx = stack.pop().unwrap();
let left = stack.last().copied().map(|x| x as i32).unwrap_or(-1);
let width = i as i32 - left - 1;
best = best.max(heights[idx] * width);
}
stack.push(i);
}
best
}
}
function largestRectangleArea(heights: number[]): number {
const stack: number[] = [];
let best = 0;
for (let i = 0; i <= heights.length; i++) {
const h = i === heights.length ? 0 : heights[i];
while (stack.length && heights[stack[stack.length - 1]] > h) {
const idx = stack.pop()!;
const left = stack.length ? stack[stack.length - 1] : -1;
const width = i - left - 1;
best = Math.max(best, heights[idx] * width);
}
stack.push(i);
}
return best;
}
Use this to step through a reusable interview workflow for this problem.
For each element, scan left (or right) to find the next greater/smaller element. The inner scan can visit up to n elements per outer iteration, giving O(n²) total comparisons. No extra space needed beyond loop variables.
Each element is pushed onto the stack at most once and popped at most once, giving 2n total operations = O(n). The stack itself holds at most n elements in the worst case. The key insight: amortized O(1) per element despite the inner while-loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Pushing without popping stale elements invalidates next-greater/next-smaller logic.
Usually fails on: Indices point to blocked elements and outputs shift.
Fix: Pop while invariant is violated before pushing current element.