📜  直方图中的最大矩形区域 |设置 2

📅  最后修改于: 2022-05-13 01:57:09.039000             🧑  作者: Mango

直方图中的最大矩形区域 |设置 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; jJava
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; jJava
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)