要知道单调栈的适用于解决什么样的问题,我们首先需要知道单调栈的作用。
单调栈分为单调递增栈和单调递减栈,
通过使用单调栈我们可以访问到最近的比他大(小)的元素。也就是说在队列或数组中,我们需要通过比较前后元素的大小关系来解决问题时我们通常使用单调栈。
题目1:直方图中的最大面积
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
题解:首先我们枚举某一根柱子 i 作为高 h=heights[i];
随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于 h。换句话说,我们需要找到左右两侧最近的高度小于 h 的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于 h,并且就是 i能够扩展到的最远范围。
class Solution:
def largestRectangleArea(self, heights):
n = len(heights)
left, right = [0] * n, [0] * n
mono_stack =[]
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
题目2:矩阵中的最大矩形
给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
题解:我们首先计算出矩阵的每个元素的左边连续 1的数量,使用二维数组 记录,其中 help[i][j]为矩阵第 i 行第 j 列元素的左边连续 1 的数量。
如图中所示,阴影部分的面积即为第二行最后一列,高度为3的矩阵,向左拓展一格的面积。这么思考的话,每一列就是第一题中的直方图。对于每一个直方图,寻找相邻的最近的高度小于其的格子。
class Solution(object):
def maximalRectangle(self, matrix):
if not matrix: return 0
m,n=len(matrix),len(matrix[0])
help=[[0 for _ in range(n)] for _ in range(m)]
for i in range(m):#构造help矩阵
count=0
for j in range(n):
if matrix[i][j]=="1":
count+=1
else:
count=0
help[i][j]=count
left_all= []
right_all=[]
for j in range(n):#每一列为直方图的横轴
left=[0 for i in range(m)]
stack=[]
for i in range(m):
while stack and help[stack[-1]][j]>=help[i][j]:
stack.pop()
if stack:
left[i] = stack[-1]
else :left[i] =-1
stack.append(i)
left_all.append(left)
for j in range(n):#每一列为直方图的横轴
right=[0 for i in range(m)]
stack=[]
for i in range(m-1,-1,-1):
while stack and help[stack[-1]][j] >= help[i][j]:
stack.pop()
if stack:
right[i] = stack[-1]
else:
right[i] = m
stack.append(i)
right_all.append(right)
result=0
for j in range(n): # 每一列为直方图的横轴
for i in range(m):
result=max(result,(right_all[j][i]-left_all[j][i]-1)*help[i][j])
return result
题目3:滑动窗口的最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
题解:将单调栈优化最大堆问题中的此题。
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
n = len(nums)
stack=[]
for i in range(k):
while stack and nums[stack[-1]]<=nums[i]:
stack.pop()
stack.append(i)
ans = [nums[stack[0]]]
for i in range(k, n):
while stack and nums[i] >= nums[stack[-1]]:
stack.pop()
stack.append(i)
while stack[0] <= i - k:
stack.pop(0)
ans.append(nums[stack[0]])
return ans
获取队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
class MaxQueue(object):
def __init__(self):
self.q=[]
self.p=[]
def max_value(self):
"""
:rtype: int
"""
return -1 if not self.p else self.p[0]
def push_back(self, value):
"""
:type value: int
:rtype: None
"""
while self.p and self.p[-1]<value:
self.p.pop()
self.p.append(value)
self.q.append(value)
def pop_front(self):
"""
:rtype: int
"""
if not self.q: return -1
if self.q[0]==self.p[0]:
self.p.pop(0)
return self.q.pop(0)
# Your MaxQueue object will be instantiated and called as such:
# obj = MaxQueue()
# param_1 = obj.max_value()
# obj.push_back(value)
# param_3 = obj.pop_front()