栈:栈(stack)又名堆栈,它是限定在表的一端进行插入和删除操作的线性表(后进先出)。这一端被称为栈顶,相对地,把另一端称为栈底。不含元素的空表称为空栈。
题目:给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:
左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效字符串。
class Solution:
def isValid(self, s: str) -> bool:
dic = {'{': '}', '[': ']', '(': ')', '?': '?'}
stack = ['?']#加一个问号, 避免空栈的判断
for c in s:
if c in dic: stack.append(c)
elif dic[stack.pop()] != c: return False
return len(stack) == 1
题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。在这里插入图片描述上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
class Solution:
def trap(self, height: List[int]) -> int:
length = len(height)
if length < 3: return 0
res, idx = 0, 0
stack = []
while idx < length:
while len(stack) > 0 and height[idx] > height[stack[-1]]:
top = stack.pop() # index of the last element in the stack
if len(stack) == 0:
break
h = min(height[stack[-1]], height[idx]) - height[top]
dist = idx - stack[-1] - 1
res += (dist * h)
stack.append(idx)
idx += 1
return res
题目:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
stack = []
heights = [0] + heights + [0]#建立一个含有哨兵的列表,首尾哨兵,假如是递增的列表数据,也可以弹栈
res = 0
for i in range(len(heights)):
#栈不为空,即有左边界,并且带入栈的高度比栈顶高度小,所以不能入栈,需要出栈计算底边长度和面积
while stack and heights[stack[-1]] > heights[i]:
temp = stack.pop()#弹栈,这里面记录的是高度的下标
#底边长度等于 i(右边界) - stack[-1](左边界) - 1
res = max(res, (i - stack[-1] -1) * heights[temp])#计算最大面积
stack.append(i)#如果栈为空则将此下标入栈
return res
题目:给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例:输入:
[
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
输出: 6
class Solution:
def maximalRectangle(self, matrix: List[List[str]]) -> int:
if not matrix: return 0
m = len(matrix)
n = len(matrix[0])
out = 0
#构建一个矩阵,每行的左右两端增加一个0
column = [[0 for i in range(n+2)] for i in range(m)]
for i in range(0, m):#将原始的字符矩阵换成数字矩阵
for j in range(1, n+1):
column[i][j] = int(matrix[i][j-1])
#对数字矩阵的每行进行判断,如果每行有值(不为0),则累加该行上一行的对应元素值
#这样做的目的是,构建m(行数)个柱形图。类似于leetcode 84题,使用84的求柱状图的最大面积
#将一个矩阵,分解为行数个柱状图,进行求解。
for i in range(1, m):
for j in range(1,n+1):
if column[i][j] == 1:
column[i][j] += column[i-1][j]
#使用的是84题,单调递增栈的做法,计算柱状图的最大面积
def max_area(s: List[int])->int:
res = 0
stack = []
for i in range(n+2):
while stack and s[stack[-1]] > s[i]:
height_index = stack.pop()
res = max(res, (i - stack[-1] - 1) * s[height_index])
stack.append(i)
return res
#循环重复m次,计算出每一行的最大柱状面积,再找一个最大值输出
for i in range(m):
out = max(out, max_area(column[i]))
return out
题目:根据 逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。说明:整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for i in range(len(tokens)):
if tokens[i] not in ['+', '-', '*', '/']:#是数字就入栈
stack.append(int(tokens[i]))
else:#是运算符就弹栈,根据运算符及进行计算
num2 = int(stack.pop())#弹出两个数字
num1 = int(stack.pop())
if tokens[i] == '*':
stack.append(num1*num2)
elif tokens[i] == '/':
stack.append(int(num1 / num2))
elif tokens[i] == '+':
stack.append(num1+num2)
elif tokens[i] == '-':
stack.append(num1-num2)
return stack[-1] #最后只有一个数字
题目:给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
class Solution:
def removeDuplicateLetters(self, s: str) -> str:
if not s: return ""
n = len(s)
stack = [s[0]]
for i in range(1,n):
if s[i] not in stack:#首先判断是否在栈中,如果已经包含了,那么就是无需再次排序
while stack and s[i] < stack[-1] and stack[-1] in s[i+1:]:#栈顶元素之后还有,并且小于待加入的元素,那么可以弹栈。
stack.pop()
stack.append(s[i])
return ''.join(stack)
题目:给定一个经过编码的字符串,返回它解码后的字符串。编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
分析:由于是字符串顺序存储,所以这里考虑使用栈来存储,
解题模板:
class Solution:
def decodeString(self, s: str) -> str:
stack = []
mul = 0
res = ""
# out = ""
for item in s:
if item == '[':#已经算出之前的重复串,得到了当前的乘数值,保存在栈中
stack.append([mul, res])
mul, res = 0, ""#保存之后清零
elif 'A' <= item <= 'Z' or 'a' <= item <= 'z':#这里有可能是大写,有可能是小写
res += item
elif item == ']':#尾括号就应该计算放前的重复串,计算后和之前的重复串相加
cur_mul, pre_res = stack.pop()#弹出上一个重复串和本次的乘数
res = pre_res + cur_mul * res
else:
mul = mul*10+int(item)#乘数有可能是多位数
return res
题目:给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。输入: num = “1432219”, k = 3。输出: “1219”。解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
class Solution:
def removeKdigits(self, num: str, k: int) -> str:
if not num: return '0'
n = len(num)
if n == k: return '0'
remain = n - k#计算最终列表中会有多少个元素,取前面的多少位
stack = [num[0]]
for i in range(1, n):
while stack and num[i] < stack[-1] and k:#栈顶元数大于待加入的元数,并且还能弹出,则弹栈
stack.pop()
k -= 1
stack.append(num[i])#直到不能弹栈就入栈,这会导致之后元数都会入栈
print(stack)
return ''.join(stack[:remain]).lstrip('0') or '0'#如果输出为空,则输出0
题目:请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。例如,给定一个列表temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
stack = []
out = [0]*len(T)
for i, t in enumerate(T):#使用枚举类型,方便计算索引和对比温度值
while stack and T[stack[-1]] < t:#当前栈不为空,并且栈顶的温度小于之后待加入的温度
out[stack.pop()] = i - stack[-1]#弹栈,填入两者之间的索引差
stack.append(i)
return out
题目:给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
stack, i = [], 0
for num in pushed:
stack.append(num)#每次加入元数入栈
while stack and stack[-1] == popped[i]:#循环判断出栈
stack.pop()
i += 1#出栈后弹栈的索引加一
return not stack
题目:给你一个括号字符串 s ,它只包含字符 ‘(’ 和 ‘)’ 。一个括号字符串被称为平衡的当它满足:任何左括号 ‘(’ 必须对应两个连续的右括号 ‘))’ 。左括号 ‘(’ 必须在对应的连续两个右括号 ‘))’ 之前。比方说 “())”, “())(())))” 和 “(())())))” 都是平衡的, “)()”, “()))” 和 “(()))” 都是不平衡的。你可以在任意位置插入字符 ‘(’ 和 ‘)’ 使字符串平衡。请你返回让 s 平衡的最少插入次数。
class Solution:
def minInsertions(self, s: str) -> int:
if not s: return 0
n = len(s)
stack = []
for i in range(n):#入栈
stack.append(s[i])
res = []
cnt = 0
while stack:#弹栈
temp = stack.pop()
if temp == ')':#右括号就入res栈
res.append(')')
else:#左括号先判断res内的奇偶
if len(res) & 1:
res.append(')')
cnt += 1
if res:
res.pop()
res.pop()
else:
cnt += 2
if res:#如果弹栈完成,就剩余的右括号配对
n = len(res)
if n & 1:
cnt += n//2 + 2
else:
cnt += n//2
return cnt