当前位置: 首页 > news >正文

直方图中最大矩形面积

原文地址:http://www.geeksforgeeks.org/largest-rectangle-under-histogram/ 
注意:本文并未对原文完整翻译,而是结合原文并根据本人理解写出,因此部分内容为完整翻译,部分内容为个人理解所写。

Largest Rectangle in Histogram 直方图中最大矩形面积

一个直方图是由许多矩形组成,在给定的直方图中找出最大的矩形面积。为了简化问题,假定所有矩形宽度都为1个单位。

例如,下面的直方图中有7个矩形,高度分别是(6,2,5,4,5,2,6)。最大的矩形面积是12(如下图所示,最大矩形面积用红色方框标出)

最大矩形面积

下面给出的解决方法时间复杂度为O(n)。矩形面积的计算公式为底*高。对于直方图中的每个矩形’x’(例如图中高度为6,2或5的矩形)以该矩形的高度为高(因为在直方图中最大矩形的高必然是某个单独矩形高),然后计算出最大矩形面积。因此接下来的问题是,若以某个矩形的高度为高,那么最终矩形的左边界和右边界在哪里?确定两个边界后就可以得到宽度,最终计算出面积。

我们从左向右遍历每个矩形,并通过一个栈来存储这些矩形高度在输入数组中的索引。每个矩形(索引)仅压入栈中一次。当输入的矩形高度小于栈顶矩形的高度,那么栈顶矩形将会被弹出,然后计算矩形面积,其中矩形面积的高为弹出单个矩形条的高。现在得到了高,接下来得到左右边界后便可计算出宽度。由于当前输入的矩形i的高度小于栈顶矩形,那么以栈顶为高的矩形右边界为i。而在当前栈中若非空,那么栈中矩形条的高度一定是小于等于弹出的矩形的高度,因此左边界就确定了。(当有多个连续的高度一样的矩形条时,计算最后一个出栈的矩形时会得到最终的面积)

最终算法步骤归纳为:

  1. 创建一个空栈
  2. 从第一个矩形条开始,对每个矩形条的高度height[i] (i的取值范围是[0,n-1])执行下面两步 
    a) 如果栈为空,或height[i]大于等于栈顶元素,那么将矩形条i压入栈中。 
    b)如果输入的矩形条高度小于栈顶元素高度,那么将栈顶元素在输入数组中的索引tp出栈,然后计算矩形面积。矩形的高为height[tp],而右边界为i,左边界为当前栈顶元素对应的索引,若栈为空,则宽度就是i。
  3. 经过计算后,栈非空,然后将栈中元素逐个弹出,并按照步骤2计算矩形面积,并且更新最大值。

总结:若输入序列是是升序,那么依次入栈,让入栈元素小于栈顶,以栈顶元素为高的矩形左边界必然是将高出栈后新的栈顶元素的位置(因为是按升序入栈)。而栈中元素是按升序排列那么以栈中任意一个元素为高,必然可以和栈顶元素构成矩形,所以当即将入栈元素小于栈顶元素时,那么右边界即是这个入栈元素的索引位置。

下面是Java的实现代码。

public class Solution {
    public int largestRectangleArea(int[] height) { Stack<Integer> s = new Stack<>(); int max_area = 0; // 最大矩形面积 int tp; // 栈顶 int area_with_top; int i = 0; int n = height.length; while (i < n) { if (s.empty() || height[s.peek()] <= height[i]) { s.push(i++); } else { tp = s.pop(); area_with_top = height[tp] * (s.empty() ? i : i - s.peek() - 1); max_area = Math.max(max_area, area_with_top); } } while (!s.empty()) { tp = s.pop(); area_with_top = height[tp] * (s.empty() ? i : i - s.peek() - 1); max_area = Math.max(max_area, area_with_top); } return max_area; } }

相关文章:

  • 百度有道雅虎的实习面试经历
  • ubuntu下spring环境搭建
  • Java教程收集
  • Xmemcached vs Spymemcached 3th(linux下测试结果和多节点下表现)
  • Linux以GB显示内存大小
  • install ubuntu on Android mobile phone
  • python3学习之练习题
  • 查询缓存和执行流程
  • POJ-3984-迷宫问题-BFS(广搜)-手写队列
  • rowid去重(删除表的重复记录)
  • 完整的solr java api操作代码块
  • scala 学习笔记--闭了个包
  • JavaScript使用正則表達式
  • Java 修改页面排序条件
  • Redis3.x HA 方案(基于 Sentinel 方式)
  • 《剑指offer》分解让复杂问题更简单
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Java的Interrupt与线程中断
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • log4j2输出到kafka
  • spring学习第二天
  • SQLServer之创建显式事务
  • Twitter赢在开放,三年创造奇迹
  • vue2.0项目引入element-ui
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 如何设计一个比特币钱包服务
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 什么是Javascript函数节流?
  • 小程序button引导用户授权
  • 转载:[译] 内容加速黑科技趣谈
  • ionic入门之数据绑定显示-1
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​力扣解法汇总946-验证栈序列
  • ​批处理文件中的errorlevel用法
  • #define与typedef区别
  • #etcd#安装时出错
  • #include
  • (1)Android开发优化---------UI优化
  • (2015)JS ES6 必知的十个 特性
  • (超详细)语音信号处理之特征提取
  • (区间dp) (经典例题) 石子合并
  • (三)docker:Dockerfile构建容器运行jar包
  • (算法)N皇后问题
  • (转)EOS中账户、钱包和密钥的关系
  • (转)Mysql的优化设置
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • *上位机的定义
  • ./configure,make,make install的作用(转)
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .Net - 类的介绍
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • .考试倒计时43天!来提分啦!
  • /proc/vmstat 详解