leetcode 每日一题 84. 柱状图中最大的矩形
单调栈
思路:
要找到最大矩形,只需要遍历每个元素得到高,得到高后再对每个元素进行左右扩散来确定对应的宽。这里确定宽的条件是如果遇到比自己低的就停止扩散。我们可以用两个数组来记录每个元素高所对应宽的左边界和右边界,确定左右边界可以采用单调栈的方式。单调栈用来记录左右边界的下标,在遍历过程中,如果遇到的下标所对应的值比栈顶元素所对应的值小时,将栈顶元素弹出直到满足当前下标所对应的值比栈顶元素大或者栈顶为空时,再将当前下标压入栈中。这里需要一个哨兵下标,左边时为-1,右边时为元素个数n,如果栈为空时,左右边界记录的下标为哨兵节点(这是因为当栈为空是,意味着当前元素可以扩散到最左边或者最右边)。在确定左右边界后,只需要把每个高所对应宽相乘,找到最大面积即可。
例如:
[2,1,5,6,2,3]
初始状态 stack = [] left = [0,0,0,0,0,0] right = [0,0,0,0,0,0]
先从左向右遍历,获得左边界
2: 此时栈为空, left = [-1,0,0,0,0,0] stack = [0]
1: 因为1<2,stack栈顶元素0出栈,此时stack为空, left = [-1,-1,0,0,0,0] stack = [1]
5: 因为5>1, 此时stack=[1] , left = [-1,-1, 1,0,0,0] stack = [1,2]
6: 因为6>5 , 此时stack = [1,2] , left = [-1,-1, 1 ,2,0,0] stack = [1,2,3]
2: 因为2<6, stack栈顶元素3出栈,此时stack=[1,2],因为2<5,stack栈顶元素2出栈,此时stack=[1],由于2>1,此时stack = [1] left = [-1,-1,1,2,1,0] stack = [1,4]
3: 因为3>2, 此时stack = [1,4], left = [-1,-1,1,2,1,4] stack = [1,4,5]
从右向左遍历,获得右边界
3:此时stack = [] right = [0,0,0,0,0,6] stack = [5]
2:因为2<3,stack栈顶元素5出栈,此时stack为空,right = [0,0,0,0,6,6] stack = [4]
6:因为6>2,此时stack = [4] , right = [0,0,0,4,6,6] stack = [4,3]
5:因为5<6,stack栈顶元素3出栈,此时stack = [4],因为5>2,此时stack = [4], right = [0,0,4,4,6,6] stack = [4,2]
1:因为1<5,stack栈顶元素2出栈,此时stack =[4],因为1<2,stack栈顶元素4出栈,此时stack为空,right = [0,6,4,4,6,6] stack = [1]
2:因为2>1,此时stack = [1] , right = [1,6,4,4,6,6] stack = [1,0]
代码:
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: n = len(heights) left, right = [0] * n, [0] * n mono_stack = list() for i in range(n): while mono_stack and heights[mono_stack[-1]] >= heights[i]: mono_stack.pop() left[i] = mono_stack[-1] if mono_stack else -1 mono_stack.append(i) mono_stack = list() for i in range(n - 1, -1, -1): while mono_stack and heights[mono_stack[-1]] >= heights[i]: mono_stack.pop() right[i] = mono_stack[-1] if mono_stack else n mono_stack.append(i) ans = max((right[i] - left[i] - 1) * heights[i] for i in range(n)) if n > 0 else 0 return ans
代码优化:(只遍历一次:从左向右确定左边界时,每当要出栈时,意味着栈顶元素所对应的值大于当前下标所对应的值,则可以确定栈顶元素的右边界。所以只需要将有边界数组全部设为哨兵,遍历左边界时,每次要出栈前,更新对应栈顶右边界值即可)
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: n = len(heights) left, right = [0] * n, [n] * n mono_stack = list() for i in range(n): while mono_stack and heights[mono_stack[-1]] >= heights[i]: right[mono_stack[-1]] = i mono_stack.pop() left[i] = mono_stack[-1] if mono_stack else -1 mono_stack.append(i) ans = max((right[i] - left[i] - 1) * heights[i] for i in range(n)) if n > 0 else 0 return ans