🧠
Using Stack to Find Next Smaller Elements
💡 This approach introduces a stack to efficiently find the nearest smaller bars on left and right for each bar, reducing the time complexity drastically.
Intuition
For each bar, find the index of the first smaller bar to the left and right. The width of the largest rectangle with this bar as height is between these two indices.
Algorithm
- Use a stack to find the index of the previous smaller element for each bar.
- Use a stack to find the index of the next smaller element for each bar.
- Calculate the width for each bar as next_smaller_index - prev_smaller_index - 1.
- Calculate area = height * width and track the maximum.
💡 This approach avoids nested loops by precomputing boundaries using stacks.
def largestRectangleArea(heights):
n = len(heights)
stack = []
prev_smaller = [-1] * n
next_smaller = [n] * n
# Find previous smaller
for i in range(n):
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
prev_smaller[i] = stack[-1] if stack else -1
stack.append(i)
stack.clear()
# Find next smaller
for i in range(n - 1, -1, -1):
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
next_smaller[i] = stack[-1] if stack else n
stack.append(i)
max_area = 0
for i in range(n):
width = next_smaller[i] - prev_smaller[i] - 1
area = heights[i] * width
max_area = max(max_area, area)
return max_area
# Example usage
if __name__ == '__main__':
print(largestRectangleArea([2,1,5,6,2,3])) # Output: 10
Line Notes
prev_smaller = [-1] * nInitialize previous smaller indices to -1 (none found)
while stack and heights[stack[-1]] >= heights[i]:Pop from stack until a smaller bar is found on the left
prev_smaller[i] = stack[-1] if stack else -1Set previous smaller index or -1 if none
stack.clear()Clear stack to reuse for next smaller element calculation
next_smaller[i] = stack[-1] if stack else nSet next smaller index or n if none
width = next_smaller[i] - prev_smaller[i] - 1Calculate width of rectangle for bar i
max_area = max(max_area, area)Update max area if current area is larger
import java.util.*;
public class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] prevSmaller = new int[n];
int[] nextSmaller = new int[n];
Arrays.fill(prevSmaller, -1);
Arrays.fill(nextSmaller, n);
Deque<Integer> stack = new ArrayDeque<>();
// Previous smaller
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
prevSmaller[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
// Next smaller
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
nextSmaller[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
int maxArea = 0;
for (int i = 0; i < n; i++) {
int width = nextSmaller[i] - prevSmaller[i] - 1;
int area = heights[i] * width;
maxArea = Math.max(maxArea, area);
}
return maxArea;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.largestRectangleArea(new int[]{2,1,5,6,2,3})); // 10
}
}
Line Notes
Arrays.fill(prevSmaller, -1);Initialize previous smaller indices to -1
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {Pop from stack until smaller bar found on left
prevSmaller[i] = stack.isEmpty() ? -1 : stack.peek();Set previous smaller index or -1 if none
stack.clear();Clear stack to reuse for next smaller element calculation
nextSmaller[i] = stack.isEmpty() ? n : stack.peek();Set next smaller index or n if none
int width = nextSmaller[i] - prevSmaller[i] - 1;Calculate width of rectangle for bar i
maxArea = Math.max(maxArea, area);Update max area if current area is larger
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> prevSmaller(n, -1);
vector<int> nextSmaller(n, n);
stack<int> st;
// Previous smaller
for (int i = 0; i < n; i++) {
while (!st.empty() && heights[st.top()] >= heights[i]) {
st.pop();
}
prevSmaller[i] = st.empty() ? -1 : st.top();
st.push(i);
}
while (!st.empty()) st.pop();
// Next smaller
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && heights[st.top()] >= heights[i]) {
st.pop();
}
nextSmaller[i] = st.empty() ? n : st.top();
st.push(i);
}
int maxArea = 0;
for (int i = 0; i < n; i++) {
int width = nextSmaller[i] - prevSmaller[i] - 1;
int area = heights[i] * width;
maxArea = max(maxArea, area);
}
return maxArea;
}
int main() {
vector<int> hist = {2,1,5,6,2,3};
cout << largestRectangleArea(hist) << endl; // 10
return 0;
}
Line Notes
vector<int> prevSmaller(n, -1);Initialize previous smaller indices to -1
while (!st.empty() && heights[st.top()] >= heights[i]) {Pop from stack until smaller bar found on left
prevSmaller[i] = st.empty() ? -1 : st.top();Set previous smaller index or -1 if none
while (!st.empty()) st.pop();Clear stack to reuse for next smaller element calculation
nextSmaller[i] = st.empty() ? n : st.top();Set next smaller index or n if none
int width = nextSmaller[i] - prevSmaller[i] - 1;Calculate width of rectangle for bar i
maxArea = max(maxArea, area);Update max area if current area is larger
function largestRectangleArea(heights) {
const n = heights.length;
const prevSmaller = new Array(n).fill(-1);
const nextSmaller = new Array(n).fill(n);
const stack = [];
// Previous smaller
for (let i = 0; i < n; i++) {
while (stack.length && heights[stack[stack.length - 1]] >= heights[i]) {
stack.pop();
}
prevSmaller[i] = stack.length ? stack[stack.length - 1] : -1;
stack.push(i);
}
stack.length = 0;
// Next smaller
for (let i = n - 1; i >= 0; i--) {
while (stack.length && heights[stack[stack.length - 1]] >= heights[i]) {
stack.pop();
}
nextSmaller[i] = stack.length ? stack[stack.length - 1] : n;
stack.push(i);
}
let maxArea = 0;
for (let i = 0; i < n; i++) {
const width = nextSmaller[i] - prevSmaller[i] - 1;
const area = heights[i] * width;
maxArea = Math.max(maxArea, area);
}
return maxArea;
}
// Example usage
console.log(largestRectangleArea([2,1,5,6,2,3])); // 10
Line Notes
const prevSmaller = new Array(n).fill(-1);Initialize previous smaller indices to -1
while (stack.length && heights[stack[stack.length - 1]] >= heights[i]) {Pop from stack until smaller bar found on left
prevSmaller[i] = stack.length ? stack[stack.length - 1] : -1;Set previous smaller index or -1 if none
stack.length = 0;Clear stack to reuse for next smaller element calculation
nextSmaller[i] = stack.length ? stack[stack.length - 1] : n;Set next smaller index or n if none
const width = nextSmaller[i] - prevSmaller[i] - 1;Calculate width of rectangle for bar i
maxArea = Math.max(maxArea, area);Update max area if current area is larger
Each element is pushed and popped at most once from the stack, so total operations are linear.
💡 For n=100000, this means roughly 200000 stack operations, which is efficient and feasible.
Interview Verdict: Accepted
This approach is efficient and accepted for large inputs, making it a strong candidate for interviews.