Leetcode刷题(12) 单调栈的应用
具体方法参考: labudadong的特殊数据结构:单调栈
该笔记是在做LeetCode的时候使用python的重写版本
496. 下一个更大元素 I
class Solution(object):
# 暴力法
def nextGreaterElement(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
hashmap = dict()
n2 = len(nums2)
# 经典的二维数组的遍历
for i in range(n2 - 1):
for j in range(i + 1, n2):
if nums2[j] > nums2[i]:
hashmap[nums2[i]] = nums2[j]
# 找到了最近的一个最大的数之后就跳出内循环,避免被后面的数给覆盖掉
break
res = []
for num in nums1:
res.append(hashmap.get(num, -1))
return res
# Next Greater Number 一般使用单调栈来解答
class Solution(object):
def nextGreaterElement(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
# 记录答案: Number --> Next Greater Number
ans = dict()
# 创建列表作为单调栈, 从栈底到栈顶元素递减
stack = list()
# 元素倒着加入栈中
n = len(nums2)
for i in range(n - 1, -1, -1):
# 先判断栈是否为空,不空的话再比较栈顶元素和当前遍历到的元素的大小
while (len(stack) > 0 and nums2[i] >= stack[-1]):
# 比当前元素小的栈内元素直接出站
stack.pop()
ans[nums2[i]] = stack[-1] if len(stack) > 0 else -1
# 当前遍历到的元素入栈接受下一次的大小判定
stack.append(nums2[i])
# 取得输出结果
res = list()
for nums1_i in nums1:
res.append(ans[nums1_i])
return res
503. 下一个更大元素 II
采用伪循环的方式构造循环
class Solution(object):
def nextGreaterElements(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
# 给原来的数组再在后面接上一截[1, 2, 1]-->[1, 2, 1, 1, 2, 1]
# 可以用循环数组来节省空间
stack = list()
n = len(nums)
ans = [-1] * n
# 假装有2 * n个元素来遍历,
# 相比第一道题, 把i替换成 i % n 就行
for i in range(n * 2 - 1, -1, -1):
# 如果stack非空,将当前元素和栈顶元素比较
while (len(stack) > 0 and nums[i % n] >= stack[-1]):
stack.pop()
# 如果栈顶不是空的就说明当前元素前还有比它大的元素,得到答案,否则得到-1
ans[i % n] = stack[-1] if len(stack) > 0 else -1
# 加入栈中,接受下一轮审判
stack.append(nums[i % n])
return ans
84. 柱状图中最大的矩形
参考Leetcode题解
暴力法
from typing import List
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
size = len(heights)
res = 0
for i in range(size):
left = i
cur_height = heights[i]
while left > 0 and heights[left - 1] >= cur_height:
left -= 1
right = i
while right < size - 1 and heights[right + 1] >= cur_height:
right += 1
max_width = right - left + 1
res = max(res, max_width * cur_height)
return res
单调栈
from typing import List
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
size = len(heights)
res = 0
stack = []
for i in range(size):
# 先顺序遍历height, 当前遍历的到的高度比栈顶的小, 栈顶元素出栈并处理这个栈顶元素
while len(stack) > 0 and heights[i] < heights[stack[-1]]:
cur_height = heights[stack.pop()]
# 反序搜索栈顶元素和cur_height高度一样的
while len(stack) > 0 and cur_height == heights[stack[-1]]:
stack.pop()
if len(stack) > 0:
# 宽度上不包括stack[-1], 所以要-1
cur_width = i - stack[-1] - 1
else:
# 都是
cur_width = i
res = max(res, cur_height * cur_width)
# (处理完最后) 当前遍历的到的高度比栈顶的大的时候, 当前高度的下标进栈
stack.append(i)
# 再反向遍历一波就是答案了, 此时的栈是 从栈顶--> 是从大到小的单调栈
while len(stack) > 0 is not None:
cur_height = heights[stack.pop()]
if len(stack) > 0:
cur_width = size - stack[-1] - 1
else:
cur_width = size
res = max(res, cur_height * cur_width)
return res
这是华为2021.08.26 pm. 7:00笔试的第二题的题解, 和上面的这题基本一样,我一开始想用单调栈,但是想不起来了....,所以最后用的暴力求解。
import sys
# 将宽大于1的箱子分为两个高度相同的子箱子
# 然后采用左右指针从中间向两边移动
def main():
sys.stdin = open('input.txt', 'r')
lines = sys.stdin.readlines()
n = len(lines)
index = 0
while index < n:
try:
line = lines[index].strip().split('],')
x = (line[0] + ']').split(',')
y = line[1].split(',')
x[0] = x[0][1]
x[-1] = x[-1][0]
x = list(map(int, x))
flag = 0
for j in x:
if j <= 0:
flag = 1
if flag == 1:
print(0)
continue
y[0] = y[0][1]
y[-1] = y[-1][0]
y = list(map(int, y))
num = len(y)
height = []
for i in range(num):
if x[i] == 1:
height.append(y[i])
elif i > 1:
height = height + [y[i]] * x[i]
hn = len(height)
maxA = 0
for i, h in enumerate(height):
left = i
right = i
thisheight = h
while left > 0 and thisheight <= height[left - 1]:
left -= 1
while right < hn - 1 and thisheight <= height[right + 1]:
right += 1
maxA = max(maxA, (right - left + 1) * thisheight)
print(maxA)
index += 1
except:
break
if __name__ == "__main__":
main()
316. 去除重复字母
class Solution(object):
def removeDuplicateLetters(self, s):
"""
:type s: str
:rtype: str
"""
instack = dict()
count = dict()
ans = []
for c in s:
count[c] = count.get(c, 0) + 1
for c in count:
instack[c] = False
for c in s:
count[c] -= 1
# 如果在里面的话就不用了, 跳过
if instack[c] == True:
continue
while len(ans) > 0 and ans[-1] > c:
# 后面还有这个将要出栈的c才将其出栈, 不然后面就遇不到了,就不是去重了
if count[ans[-1]] > 0:
instack[ans.pop()] = False
# 注意这个循环的终止条件, 不加的话会死循环(因为这个分支不会导致出栈, 会一直满足循环的条件)
elif (count[ans[-1]] == 0):
break
# 更新ans和instack
ans.append(c)
instack[c] = True
return ''.join(ans)