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.
Input: heights = [2, 1, 5, 6, 2, 3]
Output: 10
Explanation: The largest rectangle is highlighted, which has an area of 5*2 = 10 units.
Input: heights = [3, 5, 1, 7, 5, 9]
Output: 15
Explanation: The largest rectangle has an area of 5*3 = 15 units.
Input: heights = [2,4]
Pre requisite: Next greater element
One way to get the maximum rectangle area possible is by considering all the rectangles and then finding the maximum area out of those. To find the area of a rectangle, its height and width must be known. Already given the heights in the question, the only task remaining is to find the width of the rectangle.
The width of a rectangle of current height will depend on the number of rectangles on the left and right having a height greater than or equal to current height.
width = pse[ind] - nse[ind] - 1where, pse[ind] and nse[ind] represent the indices of previous and next smaller element for current index.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
/* Function to find the indices of
next smaller elements */
vector<int> findNSE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Start traversing from the back
for(int i = n - 1; i >= 0; i--) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
element is not the smaller element */
while(!st.empty() && arr[st.top()] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : n;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
previous smaller elements */
vector<int> findPSE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Traverse on the array
for(int i=0; i < n; i++) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(!st.empty() && arr[st.top()] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : -1;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
public:
// Function to find the largest rectangle area
int largestRectangleArea(vector<int> &heights) {
/* Determine the next and
previous smaller elements */
vector<int> nse = findNSE(heights);
vector<int> pse = findPSE(heights);
// To store largest area
int largestArea = 0;
// To store current area
int area;
// Traverse on the array
for(int i=0; i < heights.size(); i++) {
// Calculate current area
area = heights[i] * (nse[i] - pse[i] - 1);
// Update largest area
largestArea = max(largestArea, area);
}
// Return largest area found
return largestArea;
}
};
int main() {
vector<int> heights = {2, 1, 5, 6, 2, 3};
/* Creating an instance of
Solution class */
Solution sol;
// Function call to find the largest rectangle area
int ans = sol.largestRectangleArea(heights);
cout << "The largest rectangle area is: " << ans;
return 0;
}
import java.util.*;
class Solution {
/* Function to find the indices of
next smaller elements */
private int[] findNSE(int[] arr) {
// Size of array
int n = arr.length;
// To store the answer
int[] ans = new int[n];
// Stack
Stack<Integer> st = new Stack<>();
// Start traversing from the back
for(int i = n - 1; i >= 0; i--) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
element is not the smaller element */
while(!st.isEmpty() &&
arr[st.peek()] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = !st.isEmpty() ? st.peek() : n;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
previous smaller elements */
private int[] findPSE(int[] arr) {
// Size of array
int n = arr.length;
// To store the answer
int[] ans = new int[n];
// Stack
Stack<Integer> st = new Stack<>();
// Traverse on the array
for(int i=0; i < n; i++) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(!st.isEmpty() &&
arr[st.peek()] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = !st.isEmpty() ? st.peek() : -1;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
// Function to find the largest rectangle area
public int largestRectangleArea(int[] heights) {
/* Determine the next and
previous smaller elements */
int[] nse = findNSE(heights);
int[] pse = findPSE(heights);
// To store largest area
int largestArea = 0;
// To store current area
int area;
// Traverse on the array
for(int i=0; i < heights.length; i++) {
// Calculate current area
area = heights[i] * (nse[i]-pse[i]-1);
// Update largest area
largestArea = Math.max(largestArea, area);
}
// Return largest area found
return largestArea;
}
public static void main(String[] args) {
int[] heights = {2, 1, 5, 6, 2, 3};
/* Creating an instance of
Solution class */
Solution sol = new Solution();
// Function call to find the largest rectangle area
int ans = sol.largestRectangleArea(heights);
System.out.println("The largest rectangle area is: " + ans);
}
}
class Solution:
# Function to find the indices of
# next smaller elements
def findNSE(self, arr):
# Size of array
n = len(arr)
# To store the answer
ans = [0] * n
# Stack
st = []
# Start traversing from the back
for i in range(n - 1, -1, -1):
# Get the current element
currEle = arr[i]
# Pop the elements in the stack until
# the stack is not empty and the top
# element is not the smaller element
while st and arr[st[-1]] >= arr[i]:
st.pop()
# Update the answer
ans[i] = st[-1] if st else n
# Push the index of current
# element in the stack
st.append(i)
# Return the answer
return ans
# Function to find the indices of
# previous smaller elements
def findPSE(self, arr):
# Size of array
n = len(arr)
# To store the answer
ans = [0] * n
# Stack
st = []
# Traverse on the array
for i in range(n):
# Get the current element
currEle = arr[i]
# Pop the elements in the stack until
# the stack is not empty and the top
# elements is not the smaller element
while st and arr[st[-1]] >= arr[i]:
st.pop()
# Update the answer
ans[i] = st[-1] if st else -1
# Push the index of current
# element in the stack
st.append(i)
# Return the answer
return ans
# Function to find the largest rectangle area
def largestRectangleArea(self, heights):
# Determine the next and
# previous smaller elements
nse = self.findNSE(heights)
pse = self.findPSE(heights)
# To store largest area
largestArea = 0
# To store current area
area = 0
# Traverse on the array
for i in range(len(heights)):
# Calculate current area
area = heights[i] * (nse[i]-pse[i]-1)
# Update largest area
largestArea = max(largestArea, area)
# Return largest area found
return largestArea
# Example usage
if __name__ == "__main__":
heights = [2, 1, 5, 6, 2, 3]
# Creating an instance of Solution class
sol = Solution()
# Function call to find the largest rectangle area
ans = sol.largestRectangleArea(heights)
print("The largest rectangle area is:", ans)
class Solution {
/* Function to find the indices of
next smaller elements */
findNSE(arr) {
// Size of array
let n = arr.length;
// To store the answer
let ans = new Array(n);
// Stack
let st = [];
// Start traversing from the back
for(let i = n - 1; i >= 0; i--) {
// Get the current element
let currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
element is not the smaller element */
while(st.length &&
arr[st[st.length - 1]] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = st.length ? st[st.length - 1] : n;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
previous smaller elements */
findPSE(arr) {
// Size of array
let n = arr.length;
// To store the answer
let ans = new Array(n);
// Stack
let st = [];
// Traverse on the array
for(let i = 0; i < n; i++) {
// Get the current element
let currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(st.length &&
arr[st[st.length - 1]] >= arr[i]){
st.pop();
}
// Update the answer
ans[i] = st.length ? st[st.length - 1] : -1;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
// Function to find the largest rectangle area
largestRectangleArea(heights) {
/* Determine the next and
previous smaller elements */
let nse = this.findNSE(heights);
let pse = this.findPSE(heights);
// To store largest area
let largestArea = 0;
// To store current area
let area = 0;
// Traverse on the array
for(let i = 0; i < heights.length; i++) {
// Calculate current area
area = heights[i] * (nse[i]-pse[i]-1);
// Update largest area
largestArea = Math.max(largestArea, area);
}
// Return largest area found
return largestArea;
}
}
// Example usage
const heights = [2, 1, 5, 6, 2, 3];
// Creating an instance of Solution class
const sol = new Solution();
// Function call to find the largest rectangle area
const ans = sol.largestRectangleArea(heights);
console.log("The largest rectangle area is:", ans);
Time Complexity: O(N) (where N is the size of the given array)
Space Complexity: O(N)
In the earlier solution, precomputing the PSE and NSE arrays was contributing to multiple traversals. This can be avoiding by finding the PSE on the go. But the question of finding the NSE still remains.
width = pse[ind] - nse[ind] - 1where, pse[ind] and nse[ind] represent the indices of previous and next smaller element for current index.
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
// Function to find the largest rectangle area
int largestRectangleArea(vector<int> &heights) {
int n = heights.size(); // Size of array
// Stack
stack<int> st;
// To store largest area
int largestArea = 0;
// To store current area
int area;
/* To store the indices of next
and previous smaller elements */
int nse, pse;
// Traverse on the array
for(int i=0; i < n; i++) {
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(!st.empty() &&
heights[st.top()] >= heights[i]){
// Get the index of top of stack
int ind = st.top();
st.pop();
/* Update the index of
previous smaller element */
pse = st.empty() ? -1 : st.top();
/* Next smaller element index for
the popped element is current index */
nse = i;
// Calculate the area of the popped element
area = heights[ind] * (nse-pse-1);
// Update the maximum area
largestArea = max(largestArea, area);
}
// Push the current index in stack
st.push(i);
}
// For elements that are not popped from stack
while(!st.empty()) {
// NSE for such elements is size of array
nse = n;
// Get the index of top of stack
int ind = st.top();
st.pop();
// Update the previous smaller element
pse = st.empty() ? -1 : st.top();
// Calculate the area of the popped element
area = heights[ind] * (nse-pse-1);
// Update the maximum area
largestArea = max(largestArea, area);
}
// Return largest area found
return largestArea;
}
};
int main() {
vector<int> heights = {2, 1, 5, 6, 2, 3};
/* Creating an instance of
Solution class */
Solution sol;
// Function call to find the largest rectangle area
int ans = sol.largestRectangleArea(heights);
cout << "The largest rectangle area is: " << ans;
return 0;
}
import java.util.*;
class Solution {
// Function to find the largest rectangle area
public int largestRectangleArea(int[] heights) {
int n = heights.length; // Size of array
// Stack
Stack<Integer> st = new Stack<>();
// To store largest area
int largestArea = 0;
// To store current area
int area;
/* To store the indices of next
and previous smaller elements */
int nse, pse;
// Traverse on the array
for(int i = 0; i < n; i++) {
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(!st.isEmpty() &&
heights[st.peek()] >= heights[i]) {
// Get the index of top of stack
int ind = st.pop();
/* Update the index of
previous smaller element */
pse = st.isEmpty() ? -1 : st.peek();
/* Next smaller element index for
the popped element is current index */
nse = i;
// Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1);
// Update the maximum area
largestArea = Math.max(largestArea, area);
}
// Push the current index in stack
st.push(i);
}
// For elements that are not popped from stack
while(!st.isEmpty()) {
// NSE for such elements is size of array
nse = n;
// Get the index of top of stack
int ind = st.pop();
// Update the previous smaller element
pse = st.isEmpty() ? -1 : st.peek();
// Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1);
// Update the maximum area
largestArea = Math.max(largestArea, area);
}
// Return largest area found
return largestArea;
}
public static void main(String[] args) {
int[] heights = {2, 1, 5, 6, 2, 3};
/* Creating an instance of
Solution class */
Solution sol = new Solution();
// Function call to find the largest rectangle area
int ans = sol.largestRectangleArea(heights);
System.out.println("The largest rectangle area is: " + ans);
}
}
class Solution:
# Function to find the largest rectangle area
def largestRectangleArea(self, heights):
n = len(heights) # Size of array
# Stack
st = []
# To store largest area
largestArea = 0
# To store current area
area = 0
# To store the indices of next
# and previous smaller elements
nse = pse = 0
# Traverse on the array
for i in range(n):
# Pop the elements in the stack until
# the stack is not empty and the top
# elements is not the smaller element
while st and heights[st[-1]] >= heights[i]:
# Get the index of top of stack
ind = st.pop()
# Update the index of previous smaller element
pse = st[-1] if st else -1
# Next smaller element index for \
# the popped element is current index
nse = i
# Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1)
# Update the maximum area
largestArea = max(largestArea, area)
# Push the current index in stack
st.append(i)
# For elements that are not popped from stack
while st:
# NSE for such elements is size of array
nse = n
# Get the index of top of stack
ind = st.pop()
# Update the previous smaller element
pse = st[-1] if st else -1
# Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1)
# Update the maximum area
largestArea = max(largestArea, area)
# Return largest area found
return largestArea
# Example usage
if __name__ == "__main__":
heights = [2, 1, 5, 6, 2, 3]
# Creating an instance of Solution class
sol = Solution()
# Function call to find the largest rectangle area
ans = sol.largestRectangleArea(heights)
print("The largest rectangle area is:", ans)
class Solution {
// Function to find the largest rectangle area
largestRectangleArea(heights) {
let n = heights.length; // Size of array
// Stack
let st = [];
// To store largest area
let largestArea = 0;
// To store current area
let area;
/* To store the indices of next and
previous smaller elements */
let nse, pse;
// Traverse on the array
for(let i = 0; i < n; i++) {
/* Pop the elements in the stack until
the stack is not empty and the top
elements is not the smaller element */
while(st.length &&
heights[st[st.length - 1]] >= heights[i]) {
// Get the index of top of stack
let ind = st.pop();
/* Update the index of
previous smaller element */
pse = st.length ? st[st.length - 1] : -1;
/* Next smaller element index for
the popped element is current index */
nse = i;
// Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1);
// Update the maximum area
largestArea = Math.max(largestArea, area);
}
// Push the current index in stack
st.push(i);
}
// For elements that are not popped from stack
while(st.length) {
// NSE for such elements is size of array
nse = n;
// Get the index of top of stack
let ind = st.pop();
// Update the previous smaller element
pse = st.length ? st[st.length - 1] : -1;
// Calculate the area of the popped element
area = heights[ind] * (nse - pse - 1);
// Update the maximum area
largestArea = Math.max(largestArea, area);
}
// Return largest area found
return largestArea;
}
}
// Example usage
const heights = [2, 1, 5, 6, 2, 3];
// Creating an instance of Solution class
const sol = new Solution();
// Function call to find the largest rectangle area
const ans = sol.largestRectangleArea(heights);
console.log("The largest rectangle area is:", ans);
Time Complexity: O(N) (where N is the size of the given array)
Space Complexity: O(N)
The stack space used to find the previous smaller element during traversal can go upto O(N).
Q: Why do we need the next smaller elements for computing width?
A: The width of the rectangle for a bar extends from the nearest smaller bar on the left to the nearest smaller bar on the right. Any bar taller than the current bar does not limit its width.
Q: What if all bars have the same height?
A: The largest rectangle is the entire histogram (width = n, height = first bar).
Q: How would you extend this to a 2D matrix (maximal rectangle in a binary matrix)?
A: Convert each row into a histogram and apply this algorithm row-wise.
Q: Can we find the second-largest rectangle efficiently?
A: Yes, maintain two max areas and track valid segments dynamically.