【算法】直方图中最大面积问题

问题描述

给定一个直方图,求这个直方图中最大矩阵对应的面积是多少?

比如有个图如下 (对应的数组为:[2,1,5,6,2,3])

那么对应的最大矩形的面积应该为 10:

问题分析

这类题是很常见的一道题,也是面试当中很容易考到的一题。解决方法倒是挺多。常见的比如Divide-and-conqure等方法,复杂度也都是O(n log n)

现在有没有一种更快的方法呢?复杂度可以达到 O(n)

问题求解

解法1:分治法(divide-and-conqure)

分治法,正如其名,需要对该问题分而治之。比如还是上面给定的例子:

  • 分:[2,1,5,6,2,3] 分为 [2,1,5][6,2,3] 分别去求解
  • 合:最终的最大矩形,不是在左半直方图中,要么是在右半直方图中,或是直接跨越左右直方图。上面子问题直接对应了左半直方图中的最大矩形和右半直方图中的最大矩形。唯一剩下的就是求解第三种情况:跨越左右的直方图

针对上面跨越左右直方图的最大矩形,那么该矩形一定是跨过 [5,6]了。那么可能的情况是 [5,6], [5,6,2,3], [2,1,5,6,2,3],对应的最小高度分别为5,2,1. 因此就需要:从[5,6]出发,不断地根据最小高度扩展这个数组,并在扩展中,获得最大矩形面积即可。

代码就省略了,这个比较简单。复杂度 T(n) = 2 T(n/2) + O(n),因此 T(n) = O(n log n).

解法2:运用STL

解决2比较巧妙,思想如下:

  • 1,将数组进行排序,并能获得对应的原始的下标(index)
  • 2,不断地插入这些原始下标,并更新最大面积

排序后的原始下标为 [1,0,4,5,2,3],(分别对应原始的 [1,2,2,3,5,6])。

有一个辅助结构(set)用于存储不断加入的下标。先加入2个下标边界(-1和6)

step 0: (-1, 6)

step 1, 插入index 1: (-1, 1, 6), area: (6 - (-1) - 1) * height[1] = 6

step 2, 插入index 0: (-1, 0, 1, 6), area: (1 - (-1) - 1) * height[0] = 2

step 3, 插入index 4: (-1, 0, 1, 4, 6), area: (6 - 1 - 1) * height[4] = 8

最后算到的最大的area 为10,end。复杂度:1)开始的下标排序:O(n log n),2)后面各个step中插入下标index,并取得下标的上界和下界,这个用STL的lower_bound就可以实现,总的复杂度也是 O(n log n)。因此总的时间复杂度为 O(n log n)。

给出一份代码:

    int largestRectangleArea(vector<int> &height) {
        // write your code here
        int n = height.size();
        vector<long> v(n, 0);
        for (int i = 0; i < n; i++) {
            long l = height[i];
            l = (l << 32) | i;
            v[i] = l;
        }
        sort(v.begin(), v.end());
        set<int> st;
        st.insert(-1); st.insert(n);
        int ans = 0;
        for (int i = 0; i < n; i++) {
            int idx = 0xffffffff & v[i];
            set<int>::iterator iter = lower_bound(st.begin(), st.end(), idx);
            int high_idx = *iter;
            iter--;
            int low_idx = *iter;
            //printf("idx:%d, low:%d, high:%d\n", idx, low_idx, high_idx);
            ans = max(ans, (high_idx - low_idx - 1) * height[idx]);
            st.insert(idx);
        }
        return ans;
    }

解法3:用于Stack达到O(n)

第三种方法也是比较巧妙,运用了stack,可以将复杂度降低很多。

思想:

1,有个辅助数组leftleft[i]表示以 下标 i 为右边界的矩形,且最小高度为 height[i],所对应的最大area。

2, 有个辅助数组right,跟left类似,只是此时i为左边界。

3,stack中,保存着各个下标,这些下标对应的height,是不断递增的。当要入一个新的元素i时,不断弹出stack中的元素,直到其栈顶的元素对应的height < height[i]

这样总的过程的复杂度,就是归为:1)从左往右扫描一遍数组,元素放入stack,在某个时机该元素出stack,最后形成left数组;2)从右往左扫描一遍数组,元素放入stack,在某个时机该元素出stack,最后形成right数组. T(n) = O(n)。

附上代码:

    int largestRectangleArea(vector<int> &height) {
        stack<int> stk;
        int n = height.size();
        if (n <= 0) return 0;
        int ans = 0;
        // left[i]: the maximun area ending with index i, with lowest height as height[i];
        // right[i]: the maximun area starting with index i, with lowest height as height[i];
        vector<int> left(n, 0), right(n, 0);
        // scan from left to right
        stk.push(0); left[0] = ans;
        for (int i = 1; i < n; i++) {
            int preIdx = i;
            // all the correspoding height in stk should be increasing
            while (!stk.empty() && height[stk.top()] >= height[i]) {
                stk.pop();
            }
            // preIdx is the starting (first) index whose height is >= height[i]
            preIdx = stk.empty() ? 0 : stk.top() + 1;
            left[i] = height[i] * (i - preIdx + 1);
            stk.push(i);
        }
        // scan from right to left
        ans = max(ans, height[n-1]);
        while (!stk.empty()) stk.pop(); // clear the stk
        stk.push(n-1); right[n-1] = height[n-1];
        for (int i = n-2; i >= 0; i--) {
            int preIdx = i;
            while (!stk.empty() && height[stk.top()] >= height[i]) {
                stk.pop();
            }
            preIdx = stk.empty() ? n-1 : stk.top() - 1;
            right[i] = height[i] * (preIdx - i + 1);
            stk.push(i);
        }
        for (int i = 0; i < n; i++) ans = max(ans, right[i] + left[i] - height[i]);
        return ans;
    }
时间: 2024-10-12 13:40:22

【算法】直方图中最大面积问题的相关文章

寻找直方图中面积最大的矩形

Description A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectang

直方图中最大矩形面积

注意:本文并未对原文完整翻译,而是结合原文并根据本人理解写出,因此部分内容为完整翻译,部分内容为个人理解所写. Largest Rectangle in Histogram 直方图中最大矩形面积 一个直方图是由许多矩形组成,在给定的直方图中找出最大的矩形面积.为了简化问题,假定所有矩形宽度都为1个单位. 例如,下面的直方图中有7个矩形,高度分别是(6,2,5,4,5,2,6).最大的矩形面积是12(如下图所示,最大矩形面积用红色方框标出) 下面给出的解决方法时间复杂度为O(n).矩形面积的计算公

计算直方图中最大矩形面积

题目是计算直方图中的最大矩形面积,下面是我的做法,我在网上也看到有人说可以通过栈的方式来解决,因为时间问题,并没有马上尝试,下回有时间在尝试下吧!! 还有这题有变式:计算矩阵中最大的矩形面积,其中矩阵中元素只能为1和0,代码下次补发吧!! 代码如下: #include<iostream>using namespace std; int maxSquare(const int pos,const int n,const int height[]){ if(n==1) return height[

Leetcode 84 求直方图中矩形的最大面积

题目描述 Leetcode 84 给定 n 个正整数的列表,表示矩形的高度,表示直方图.每一个给出的矩形宽度是 1,找到在直方图里最大的矩形面积. 如图中给出的直方图,宽度是 1,给出的高度是 [2,1,5,6,2,3]. 可以在直方图中找出最大的隐藏面积,答案是 10. Input: [2,1,5,6,2,3] Output: 10 题目分析 解法一: 最后矩形的最大面积,肯定是以某个矩形为最矮高度,向左向右可扩展的最大面积. 举例子来说,假设以 6 为当前直方图中的最矮高度,分别向左和向右扩

AcWing:131. 直方图中最大的矩形(贪心 + 单调栈)

直方图是由在公共基线处对齐的一系列矩形组成的多边形. 矩形具有相等的宽度,但可以具有不同的高度. 例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1: 通常,直方图用于表示离散分布,例如,文本中字符的频率. 现在,请你计算在公共基线处对齐的直方图中最大矩形的面积. 图例右图显示了所描绘直方图的最大对齐矩形. 输入格式 输入包含几个测试用例. 每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目. 然后跟随n个整数h1,…,hn

直方图最大矩阵面积

直方图最大矩阵面积法: 给定n个非负整数,表示直方图的方柱的高度,同时,每个方柱的宽度假定都为1,找出直方图中最大的矩形面积. 如:给定高度为:2,1,5,6,2,3,最大面积为10. 程序实现: 1 #include <iostream> 2 #include <stack> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 int LargestRectangle

[LeetCode] Largest Rectangle in Histogram 直方图中最大的矩形

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram. Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]. The largest

1104: 零起点学算法11——求梯形面积

1104: 零起点学算法11--求梯形面积 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 6473  Accepted: 1875[Submit][Status][Web Board] Description 水题 Input 输入3个浮点数,分别表示上底.下底和高,中间用逗号隔开(题目包含多组数据) Output 输出梯形的面积,保留2位小数 Sample Input 2,4,5 Sample

编程算法 - 数组中的逆序对 代码(C)

数组中的逆序对 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 在数组中的两个数字如果前面一个数字大于后面的数字, 则这两个数字组成一个逆序对. 输入一个数组, 求出这个数组中的逆序对的总数. 使用归并排序的方法, 辅助空间一个排序的数组, 依次比较前面较大的数字, 算出整体的逆序对数, 不用逐个比较. 时间复杂度: O(nlogn) 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: