• 柱状图中最大的矩形


    1.题目描述

    给定n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1
    求在该柱状图中,能够勾勒出来的矩形的最大面积。
    示例:

    输入: [2,1,5,6,2,3]
    输出: 10
    

    2.题解

    public int largestRectangleArea(int[] heights) {
    	int n = heights.length;
    	int[] left = new int[n];
    	int[] right = new int[n];
    	
    	Stack<Integer> mono_stack = new Stack<Integer>();
    	for (int i = 0; i < n; ++i) {
    		while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
    			mono_stack.pop();
    		}
    		left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
    		mono_stack.push(i);
    	}
    
    	mono_stack.clear();
    	for (int i = n - 1; i >= 0; --i) {
    		while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
    			mono_stack.pop();
    		}
    		right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
    		mono_stack.push(i);
    	}
    	
    	int ans = 0;
    	for (int i = 0; i < n; ++i) {
    		ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
    	}
    	return ans;
    }
    

    遍历每根柱子,这根柱子作为所能形成的最大矩形的一部分,计算该矩形的面积。
    该矩形两边的柱子高度肯定都小于矩形内的所有柱子的高度。
    于是,需要计算每根柱子的左右边界。值得注意的是,这里的第一根柱子的左边界为-1,最后一根柱子的右边界为n(这里是柱子个数)。我们称这里的-1n为哨兵,它们用于处理边界值的问题。
    [2,1,5,6,2,3]为例:
    遍历第一根柱子,其左边界为-1
    遍历第二根柱子,由于其高度小于等于第一根柱子的高度,所以第二根柱子的左边界为-1
    遍历第三根柱子,由于其高度大于第二根柱子的高度,所以第三根柱子的左边界为第二根柱子所在的位置1
    如果每根柱子的高度单调递增,就将这些柱子(位置)压入栈中。当柱子i的高度小于前一根柱子的高度,前一根柱子出栈,只要栈中的柱子高度大于等于柱子i的高度,就依次出栈,直到栈中的柱子高度小于柱子i的高度或者栈为空。这样就可以找到每根柱子的左边界。
    注:这种方法跟接雨水的方法二很相似。

    只遍历一次就确定柱子的左右边界的方法:

    public int largestRectangleArea(int[] heights) {
    	int n = heights.length;
    	int[] left = new int[n];
    	int[] right = new int[n];
    	Arrays.fill(right, n);
    	
    	Stack<Integer> mono_stack = new Stack<Integer>();
    	for (int i = 0; i < n; ++i) {
    		while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
    			right[mono_stack.peek()] = i;
    			mono_stack.pop();
    		}
    		left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
    		mono_stack.push(i);
    	}
    	
    	int ans = 0;
    	for (int i = 0; i < n; ++i) {
    		ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
    	}
    	return ans;
    }
    

    在确定柱子i的左边界时,栈中的柱子可能出栈,注意到这些出栈的柱子的右边界就是柱子i的位置。
    需要注意的是柱子出栈的条件是heights[mono_stack.peek()] >= heights[i],以[2,1,5,5,6,2,3]为例,计算出的right数组为[1,7,3,5,5,7,7],注意到这里计算得到的第三根柱子的右边界是3,而正确的右边界应该是5。但是这不影响计算矩形的最大面积。
    因为柱子出栈后,也会把当前遍历的柱子压入栈中,这样计算出的矩形最右边那根柱子的右边界肯定是正确的。
    这里用第四根柱子计算出最大矩形的面积,而这个最大矩形的最右边那根柱子是第五跟柱子。
    因为遍历第五根柱子时,第四根柱子没有出栈,这样在遍历第六根柱子时,第五根柱子和第四根柱子依次出栈,所以这里第四根柱子和第五根柱子的右边界都是正确的。
    注意到第五根柱子的左边界不是最大矩形的左边界,所以这里只能用第四根柱子计算最大矩形的面积。
    这里的关键是找到最大矩形中的某根柱子,该柱子的左右边界正好是最大矩形的左右边界。
    参考:

  • 相关阅读:
    【Object-C】判断指针类型,isKindOfxxx
    【Object-C】继承,super关键字
    Echart的angularjs封装
    ng-validate
    random background
    新发现。css3控制浏览器滚动条的样式
    如何灵活利用免费开源图标字体-IcoMoon篇
    干货分享:让你分分钟学会 javascript 闭包
    webpack
    css黑魔法
  • 原文地址:https://www.cnblogs.com/gzhjj/p/14262078.html
Copyright © 2020-2023  润新知