直方图中的最大矩形区域 |设置 2
在给定的直方图中找到可能的最大矩形区域,其中最大的矩形可以由许多连续的条组成。为简单起见,假设所有条具有相同的宽度并且宽度为 1 个单位。
例如,考虑以下具有 7 个高度的柱状图 {6, 2, 5, 4, 5, 1, 6}。可能的最大矩形是 12(见下图,最大面积矩形以红色突出显示)
我们已经讨论了针对这个问题的基于分而治之的 O(nLogn) 解决方案。在这篇文章中,讨论了 O(n) 时间解决方案。与上一篇文章一样,为简单起见,假设所有条形的宽度为 1。对于每个条形“x”,我们计算以“x”为矩形中最小条形的面积。如果我们计算每个条形“x”的面积并找到所有面积的最大值,我们的任务就完成了。如何以“x”为最小条计算面积?我们需要知道“x”左侧第一个较小(小于“x”)柱的索引和“x”右侧第一个较小柱的索引。让我们将这些索引分别称为“左索引”和“右索引”。
我们从左到右遍历所有的bar,维护一堆bar。每个柱被推入堆叠一次。当看到一个较小高度的条时,从堆栈中弹出一个条。当一个条被弹出时,我们将弹出条的区域计算为最小条。我们如何获得弹出条的左右索引——当前索引告诉我们“右索引”,堆栈中前一项的索引是“左索引”。以下是完整的算法。
1)创建一个空栈。
2)从第一个柱开始,对每个柱 'hist[i]' 执行以下操作,其中 'i' 从 0 变化到 n-1。
…… a)如果 stack 为空或 hist[i] 高于栈顶的 bar,则 push 'i' 入栈。
…… b)如果该条小于栈顶,则在栈顶大于栈顶时继续移除栈顶。让移除的柱为 hist[tp]。以 hist[tp] 作为最小条计算矩形的面积。对于 hist[tp],“左索引”是堆栈中的前一个(在 tp 之前)项目,“右索引”是“i”(当前索引)。
3)如果堆栈不为空,则一个接一个地从堆栈中移除所有柱,并对每个移除的柱执行步骤 2.b。
以下是上述算法的实现。
C++
// C++ program to find maximum rectangular area in
// linear time
#include
using namespace std;
// The main function to find the maximum rectangular
// area under given histogram with n bars
int getMaxArea(int hist[], int n)
{
// Create an empty stack. The stack holds indexes
// of hist[] array. The bars stored in stack are
// always in increasing order of their heights.
stack s;
int max_area = 0; // Initialize max area
int tp; // To store top of stack
int area_with_top; // To store area with top bar
// as the smallest bar
// Run through all bars of given histogram
int i = 0;
while (i < n)
{
// If this bar is higher than the bar on top
// stack, push it to stack
if (s.empty() || hist[s.top()] <= hist[i])
s.push(i++);
// If this bar is lower than top of stack,
// then calculate area of rectangle with stack
// top as the smallest (or minimum height) bar.
// 'i' is 'right index' for the top and element
// before top in stack is 'left index'
else
{
tp = s.top(); // store the top index
s.pop(); // pop the top
// Calculate the area with hist[tp] stack
// as smallest bar
area_with_top = hist[tp] * (s.empty() ? i :
i - s.top() - 1);
// update max area, if needed
if (max_area < area_with_top)
max_area = area_with_top;
}
}
// Now pop the remaining bars from stack and calculate
// area with every popped bar as the smallest bar
while (s.empty() == false)
{
tp = s.top();
s.pop();
area_with_top = hist[tp] * (s.empty() ? i :
i - s.top() - 1);
if (max_area < area_with_top)
max_area = area_with_top;
}
return max_area;
}
// Driver program to test above function
int main()
{
int hist[] = {6, 2, 5, 4, 5, 1, 6};
int n = sizeof(hist)/sizeof(hist[0]);
cout << "Maximum area is " << getMaxArea(hist, n);
return 0;
}
Java
//Java program to find maximum rectangular area in linear time
import java.util.Stack;
public class RectArea
{
// The main function to find the maximum rectangular area under given
// histogram with n bars
static int getMaxArea(int hist[], int n)
{
// Create an empty stack. The stack holds indexes of hist[] array
// The bars stored in stack are always in increasing order of their
// heights.
Stack s = new Stack<>();
int max_area = 0; // Initialize max area
int tp; // To store top of stack
int area_with_top; // To store area with top bar as the smallest bar
// Run through all bars of given histogram
int i = 0;
while (i < n)
{
// If this bar is higher than the bar on top stack, push it to stack
if (s.empty() || hist[s.peek()] <= hist[i])
s.push(i++);
// If this bar is lower than top of stack, then calculate area of rectangle
// with stack top as the smallest (or minimum height) bar. 'i' is
// 'right index' for the top and element before top in stack is 'left index'
else
{
tp = s.peek(); // store the top index
s.pop(); // pop the top
// Calculate the area with hist[tp] stack as smallest bar
area_with_top = hist[tp] * (s.empty() ? i : i - s.peek() - 1);
// update max area, if needed
if (max_area < area_with_top)
max_area = area_with_top;
}
}
// Now pop the remaining bars from stack and calculate area with every
// popped bar as the smallest bar
while (s.empty() == false)
{
tp = s.peek();
s.pop();
area_with_top = hist[tp] * (s.empty() ? i : i - s.peek() - 1);
if (max_area < area_with_top)
max_area = area_with_top;
}
return max_area;
}
// Driver program to test above function
public static void main(String[] args)
{
int hist[] = { 6, 2, 5, 4, 5, 1, 6 };
System.out.println("Maximum area is " + getMaxArea(hist, hist.length));
}
}
//This code is Contributed by Sumit Ghosh
Python3
# Python3 program to find maximum
# rectangular area in linear time
def max_area_histogram(histogram):
# This function calculates maximum
# rectangular area under given
# histogram with n bars
# Create an empty stack. The stack
# holds indexes of histogram[] list.
# The bars stored in the stack are
# always in increasing order of
# their heights.
stack = list()
max_area = 0 # Initialize max area
# Run through all bars of
# given histogram
index = 0
while index < len(histogram):
# If this bar is higher
# than the bar on top
# stack, push it to stack
if (not stack) or (histogram[stack[-1]] <= histogram[index]):
stack.append(index)
index += 1
# If this bar is lower than top of stack,
# then calculate area of rectangle with
# stack top as the smallest (or minimum
# height) bar.'i' is 'right index' for
# the top and element before top in stack
# is 'left index'
else:
# pop the top
top_of_stack = stack.pop()
# Calculate the area with
# histogram[top_of_stack] stack
# as smallest bar
area = (histogram[top_of_stack] *
((index - stack[-1] - 1)
if stack else index))
# update max area, if needed
max_area = max(max_area, area)
# Now pop the remaining bars from
# stack and calculate area with
# every popped bar as the smallest bar
while stack:
# pop the top
top_of_stack = stack.pop()
# Calculate the area with
# histogram[top_of_stack]
# stack as smallest bar
area = (histogram[top_of_stack] *
((index - stack[-1] - 1)
if stack else index))
# update max area, if needed
max_area = max(max_area, area)
# Return maximum area under
# the given histogram
return max_area
# Driver Code
hist = [6, 2, 5, 4, 5, 1, 6]
print("Maximum area is",
max_area_histogram(hist))
# This code is contributed
# by Jinay Shah
C#
// C# program to find maximum
// rectangular area in linear time
using System;
using System.Collections.Generic;
class GFG
{
// The main function to find the
// maximum rectangular area under
// given histogram with n bars
public static int getMaxArea(int[] hist,
int n)
{
// Create an empty stack. The stack
// holds indexes of hist[] array
// The bars stored in stack are always
// in increasing order of their heights.
Stack s = new Stack();
int max_area = 0; // Initialize max area
int tp; // To store top of stack
int area_with_top; // To store area with top
// bar as the smallest bar
// Run through all bars of
// given histogram
int i = 0;
while (i < n)
{
// If this bar is higher than the
// bar on top stack, push it to stack
if (s.Count == 0 || hist[s.Peek()] <= hist[i])
{
s.Push(i++);
}
// If this bar is lower than top of stack,
// then calculate area of rectangle with
// stack top as the smallest (or minimum
// height) bar. 'i' is 'right index' for
// the top and element before top in stack
// is 'left index'
else
{
tp = s.Peek(); // store the top index
s.Pop(); // pop the top
// Calculate the area with hist[tp]
// stack as smallest bar
area_with_top = hist[tp] *
(s.Count == 0 ? i : i - s.Peek() - 1);
// update max area, if needed
if (max_area < area_with_top)
{
max_area = area_with_top;
}
}
}
// Now pop the remaining bars from
// stack and calculate area with every
// popped bar as the smallest bar
while (s.Count > 0)
{
tp = s.Peek();
s.Pop();
area_with_top = hist[tp] *
(s.Count == 0 ? i : i - s.Peek() - 1);
if (max_area < area_with_top)
{
max_area = area_with_top;
}
}
return max_area;
}
// Driver Code
public static void Main(string[] args)
{
int[] hist = new int[] {6, 2, 5, 4, 5, 1, 6};
Console.WriteLine("Maximum area is " +
getMaxArea(hist, hist.Length));
}
}
// This code is contributed by Shrikant13
C++
#include
using namespace std;
//Function to find largest rectangular area possible in a given histogram.
int getMaxArea(int arr[], int n)
{
// Your code here
//we create an empty stack here.
stack s;
//we push -1 to the stack because for some elements there will be no previous
//smaller element in the array and we can store -1 as the index for previous smaller.
s.push(-1);
int area = arr[0];
int i = 0;
//We declare left_smaller and right_smaller array of size n and initialize them with -1 and n as their default value.
//left_smaller[i] will store the index of previous smaller element for ith element of the array.
//right_smaller[i] will store the index of next smaller element for ith element of the array.
vector left_smaller(n, -1), right_smaller(n, n);
while(iarr[i]){
//if the current element is smaller than element with index stored on the
//top of stack then, we pop the top element and store the current element index
//as the right_smaller for the popped element.
right_smaller[s.top()] = i;
s.pop();
}
if(i>0&&arr[i]==arr[i-1]){
//we use this condition to avoid the unnecessary loop to find the left_smaller.
//since the previous element is same as current element, the left_smaller will always be the same for both.
left_smaller[i] = left_smaller[i-1];
}else{
//Element with the index stored on the top of the stack is always smaller than the current element.
//Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.top();
}
s.push(i);
i++;
}
for(int j = 0; j
Java
import java.util.*;
import java.lang.*;
import java.io.*;
public class RectArea
{
//Function to find largest rectangular area possible in a given histogram.
public static int getMaxArea(int arr[], int n)
{
// your code here
//we create an empty stack here.
Stack s = new Stack<>();
//we push -1 to the stack because for some elements there will be no previous
//smaller element in the array and we can store -1 as the index for previous smaller.
s.push(-1);
int max_area = arr[0];
//We declare left_smaller and right_smaller array of size n and initialize them with -1 and n as their default value.
//left_smaller[i] will store the index of previous smaller element for ith element of the array.
//right_smaller[i] will store the index of next smaller element for ith element of the array.
int left_smaller[] = new int[n];
int right_smaller[] = new int[n];
for (int i = 0; i < n; i++){
left_smaller[i] = -1;
right_smaller[i] = n;
}
int i = 0;
while (i < n)
{
while(!s.empty()&&s.peek()!=-1&&arr[i]0&&arr[i]==arr[(i-1)]){
//we use this condition to avoid the unnecessary loop to find the left_smaller.
//since the previous element is same as current element, the left_smaller will always be the same for both.
left_smaller[i] = left_smaller[(int)(i-1)];
}else{
//Element with the index stored on the top of the stack is always smaller than the current element.
//Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.peek();
}
s.push(i);
i++;
}
for(i = 0; i
Python3
#this is single line comment
"""
this
is
multiline
comment
"""
def getMaxArea(arr):
s = [-1]
n = len(arr)
area = 0
i = 0
left_smaller = [-1]*n
right_smaller = [n]*n
while i < n:
while s and (s[-1] != -1) and (arr[s[-1]] > arr[i]):
right_smaller[s[-1]] = i
s.pop()
if((i > 0) and (arr[i] == arr[i-1])):
left_smaller[i] = left_smaller[i-1]
else:
left_smaller[i] = s[-1]
s.append(i)
i += 1
for j in range(0, n):
area = max(area, arr[j]*(right_smaller[j]-left_smaller[j]-1))
return area
hist = [6, 2, 5, 4, 5, 1, 6]
print("maxArea = ", getMaxArea(hist))
#This code is contributed by Arunit Kumar
C#
using System;
using System.Collections.Generic;
public class RectArea
{
// Function to find largest rectangular area possible in a given histogram.
public static int getMaxArea(int []arr, int n)
{
// your code here
// we create an empty stack here.
Stack s = new Stack();
// we push -1 to the stack because for some elements there will be no previous
// smaller element in the array and we can store -1 as the index for previous
// smaller.
s.Push(-1);
int max_area = arr[0];
// We declare left_smaller and right_smaller array of size n and initialize them
// with -1 and n as their default value.
// left_smaller[i] will store the index of previous smaller element for ith
// element of the array.
// right_smaller[i] will store the index of next smaller element for ith element
// of the array.
int []left_smaller = new int[n];
int []right_smaller = new int[n];
for (int j = 0; j < n; j++) {
left_smaller[j] = -1;
right_smaller[j] = n;
}
int i = 0;
while (i < n) {
while (s.Count !=0 && s.Peek() != -1 && arr[i] < arr[s.Peek()])
{
// if the current element is smaller than element with index stored on the
// top of stack then, we pop the top element and store the current element index
// as the right_smaller for the popped element.
right_smaller[s.Peek()] = (int) i;
s.Pop();
}
if (i > 0 && arr[i] == arr[(i - 1)])
{
// we use this condition to avoid the unnecessary loop to find the left_smaller.
// since the previous element is same as current element, the left_smaller will
// always be the same for both.
left_smaller[i] = left_smaller[(int) (i - 1)];
}
else
{
// Element with the index stored on the top of the stack is always smaller than
// the current element.
// Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.Peek();
}
s.Push(i);
i++;
}
for (i = 0; i < n; i++)
{
// here we find area with every element as the smallest element in their range
// and compare it with the previous area.
// in this way we get our max Area form this.
max_area = Math.Max(max_area, arr[i] * (right_smaller[i] - left_smaller[i] - 1));
}
return max_area;
}
public static void Main(String[] args) {
int []hist = { 6, 2, 5, 4, 5, 1, 6 };
Console.WriteLine("Maximum area is " + getMaxArea(hist, hist.Length));
}
}
// This code is contributed by Rajput-Ji
Maximum area is 12
时间复杂度:由于每根柱只被推送和弹出一次,因此该方法的时间复杂度为 O(n)。
另一种有效方法:通过在O(n) 时间复杂度和 O(n) 辅助空间中为每个元素找到下一个较小元素和前一个较小元素。
第 1 步:首先我们将取两个数组left_smaller[]和right_smaller[]并分别用 -1 和 n 对其进行初始化。
第 2 步:对于每个元素,我们将前一个较小元素和下一个较小元素的索引分别存储在 left_smaller[] 和 right_smaller[] 数组中。
(这将花费 O(n) 时间)。
第 3 步:现在对于每个元素,我们将通过将第 i 个元素作为 left_smaller[i] 和 right_smaller[i] 范围内的最小值并将其乘以 left_smaller[i] 和 right_smaller[i] 的差来计算面积。
步骤 4 :我们可以找到步骤 3 中计算的所有面积的最大值,以获得所需的最大面积。
C++
#include
using namespace std;
//Function to find largest rectangular area possible in a given histogram.
int getMaxArea(int arr[], int n)
{
// Your code here
//we create an empty stack here.
stack s;
//we push -1 to the stack because for some elements there will be no previous
//smaller element in the array and we can store -1 as the index for previous smaller.
s.push(-1);
int area = arr[0];
int i = 0;
//We declare left_smaller and right_smaller array of size n and initialize them with -1 and n as their default value.
//left_smaller[i] will store the index of previous smaller element for ith element of the array.
//right_smaller[i] will store the index of next smaller element for ith element of the array.
vector left_smaller(n, -1), right_smaller(n, n);
while(iarr[i]){
//if the current element is smaller than element with index stored on the
//top of stack then, we pop the top element and store the current element index
//as the right_smaller for the popped element.
right_smaller[s.top()] = i;
s.pop();
}
if(i>0&&arr[i]==arr[i-1]){
//we use this condition to avoid the unnecessary loop to find the left_smaller.
//since the previous element is same as current element, the left_smaller will always be the same for both.
left_smaller[i] = left_smaller[i-1];
}else{
//Element with the index stored on the top of the stack is always smaller than the current element.
//Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.top();
}
s.push(i);
i++;
}
for(int j = 0; j
Java
import java.util.*;
import java.lang.*;
import java.io.*;
public class RectArea
{
//Function to find largest rectangular area possible in a given histogram.
public static int getMaxArea(int arr[], int n)
{
// your code here
//we create an empty stack here.
Stack s = new Stack<>();
//we push -1 to the stack because for some elements there will be no previous
//smaller element in the array and we can store -1 as the index for previous smaller.
s.push(-1);
int max_area = arr[0];
//We declare left_smaller and right_smaller array of size n and initialize them with -1 and n as their default value.
//left_smaller[i] will store the index of previous smaller element for ith element of the array.
//right_smaller[i] will store the index of next smaller element for ith element of the array.
int left_smaller[] = new int[n];
int right_smaller[] = new int[n];
for (int i = 0; i < n; i++){
left_smaller[i] = -1;
right_smaller[i] = n;
}
int i = 0;
while (i < n)
{
while(!s.empty()&&s.peek()!=-1&&arr[i]0&&arr[i]==arr[(i-1)]){
//we use this condition to avoid the unnecessary loop to find the left_smaller.
//since the previous element is same as current element, the left_smaller will always be the same for both.
left_smaller[i] = left_smaller[(int)(i-1)];
}else{
//Element with the index stored on the top of the stack is always smaller than the current element.
//Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.peek();
}
s.push(i);
i++;
}
for(i = 0; i
Python3
#this is single line comment
"""
this
is
multiline
comment
"""
def getMaxArea(arr):
s = [-1]
n = len(arr)
area = 0
i = 0
left_smaller = [-1]*n
right_smaller = [n]*n
while i < n:
while s and (s[-1] != -1) and (arr[s[-1]] > arr[i]):
right_smaller[s[-1]] = i
s.pop()
if((i > 0) and (arr[i] == arr[i-1])):
left_smaller[i] = left_smaller[i-1]
else:
left_smaller[i] = s[-1]
s.append(i)
i += 1
for j in range(0, n):
area = max(area, arr[j]*(right_smaller[j]-left_smaller[j]-1))
return area
hist = [6, 2, 5, 4, 5, 1, 6]
print("maxArea = ", getMaxArea(hist))
#This code is contributed by Arunit Kumar
C#
using System;
using System.Collections.Generic;
public class RectArea
{
// Function to find largest rectangular area possible in a given histogram.
public static int getMaxArea(int []arr, int n)
{
// your code here
// we create an empty stack here.
Stack s = new Stack();
// we push -1 to the stack because for some elements there will be no previous
// smaller element in the array and we can store -1 as the index for previous
// smaller.
s.Push(-1);
int max_area = arr[0];
// We declare left_smaller and right_smaller array of size n and initialize them
// with -1 and n as their default value.
// left_smaller[i] will store the index of previous smaller element for ith
// element of the array.
// right_smaller[i] will store the index of next smaller element for ith element
// of the array.
int []left_smaller = new int[n];
int []right_smaller = new int[n];
for (int j = 0; j < n; j++) {
left_smaller[j] = -1;
right_smaller[j] = n;
}
int i = 0;
while (i < n) {
while (s.Count !=0 && s.Peek() != -1 && arr[i] < arr[s.Peek()])
{
// if the current element is smaller than element with index stored on the
// top of stack then, we pop the top element and store the current element index
// as the right_smaller for the popped element.
right_smaller[s.Peek()] = (int) i;
s.Pop();
}
if (i > 0 && arr[i] == arr[(i - 1)])
{
// we use this condition to avoid the unnecessary loop to find the left_smaller.
// since the previous element is same as current element, the left_smaller will
// always be the same for both.
left_smaller[i] = left_smaller[(int) (i - 1)];
}
else
{
// Element with the index stored on the top of the stack is always smaller than
// the current element.
// Therefore the left_smaller[i] will always be s.top().
left_smaller[i] = s.Peek();
}
s.Push(i);
i++;
}
for (i = 0; i < n; i++)
{
// here we find area with every element as the smallest element in their range
// and compare it with the previous area.
// in this way we get our max Area form this.
max_area = Math.Max(max_area, arr[i] * (right_smaller[i] - left_smaller[i] - 1));
}
return max_area;
}
public static void Main(String[] args) {
int []hist = { 6, 2, 5, 4, 5, 1, 6 };
Console.WriteLine("Maximum area is " + getMaxArea(hist, hist.Length));
}
}
// This code is contributed by Rajput-Ji
maxArea = 12
时间复杂度: O(n)
空间复杂度: O(n)