1. 有效的括号(20)
这道题是检验一列括号是否左右对称完整的问题, 考虑用栈的方法解决, 即遍历所有括号, 遇到左括号就把对应的右括号放入栈内, 遇到右括号就弹出栈口的左括号, 看是否对应, 有以下3种错误的方式:
1. 字符串遍历完了, 栈不为空 -- 左括号多了
2. 右括号与栈口弹出的不匹配, 则出现了匹配错误
3. 没遍历完, 栈就空了-- 右括号多了
stack = []
for i in s:
if i == "(":
stack.append(")")
elif i == "[":
stack.append("]")
elif i == "{":
stack.append("}")
elif not stack or stack[-1] != i:
return False
else:
stack.pop()
return not stack
首先如果是左括号则把右括号放入栈内, 然后检验两种情况, 第一种是不匹配第二种是栈为空, 栈如果为空则是情况3, 不匹配则是情况2, 都return false, 接下来仅需要弹出右括号即可
最后如果遍历完成, 栈还不为空则return false
还有一种写法是用字典的key和value存左右括号, 没必要, 在这里就不赘述
2. 删除字符串中所有相邻重复项(1047)
这道题需要注意的问题是, 和消消乐游戏一样, 如果删除以后又有相同字符串, 需要继续删除. 比如abbaca中, bb消除, aaca中aa消除, 最后剩余ca是要得到的结果
1.栈
用栈解决这道问题很合适, 每遍历一个元素就存入到栈中, 满足消除条件以后弹出, 看最后栈里的是什么
res = []
for i in s:
if res and res[-1] == i:
res.pop()
else:
res.append(i)
return "".join(res)
如果栈为空且栈口元素和遍历的元素相同就弹出, 否则就加入栈中. 最后用合并字符串
2.双指针
两个指针, 起始位置都是字符串的开头, fast遍历列表的元素,slow记录符合要求的元素, 当出现res[slow] == res[slow - 1]时, 说明当前字符与上一个字符重复, 则slow-1, 也就是slow回退一格, 相当于移除重复元素, 此时指向的元素要在fast+1以后用fast值覆盖
如果不重复, 则slow+=1, 即slow指针前进一格, 保留当前字符
def removeDuplicates(self, s: str) -> str:
res = list(s) # 将字符串转换为列表,以便进行原地修改
slow = fast = 0 # 初始化两个指针,slow和fast都指向列表的起始位置
length = len(res) # 获取列表的长度
while fast < length:
res[slow] = res[fast] # 将fast指针指向的元素复制到slow指针的位置
if slow > 0 and res[slow] == res[slow - 1]:
# 如果slow指针前一个元素和当前元素相同,说明有重复,slow指针回退一格
slow -= 1
else:
# 如果没有重复,slow指针前进一格
slow += 1
fast += 1 # fast指针前进一格
return ''.join(res[0: slow]) # 返回去重后的结果,将列表的前slow个元素转换为字符串
3. 逆波兰表达式
正常我们习惯看到的是中置表达式, 如(1+2)*(3+4), 写成二叉树的形式如下:
按照左中右的顺序形成后缀表达式, 即: 12+34+*
如何计算这个式子呢, 考虑用栈来解决:
1. 遇到数字就放到栈中
2.遇到操作符就取出两个数字运算后把结果加入栈(类比消除就是两个元素一个操作符消除掉, 得到的数字加入栈中)
from operator import add, sub, mul
class Solution(object):
op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y) if x * y > 0 else -(-x // y)}
def evalRPN(self, tokens):
"""
:type tokens: List[str]
:rtype: int
"""
stack = list()
for i in tokens:
if i not in ['+', '-', '*', '/']:
stack.append(int(i))
else:
num2 = stack.pop()
num1 = stack.pop()
stack.append(self.op_map[i](num1, num2))
return stack.pop()
这个代码有些需要注意的细节
1. 要把操作数i压为整数再放入栈中, 否则他是字符串, 而add等其他运算符函数期望的是整数
2. 弹出操作数时,先弹出的应该作为运算符的第一个操作数,后弹出的作为第二个操作数。这是因为在逆波兰表达式(RPN)中,操作数是先入栈的,后出栈的应该是右操作数。比如遇到2和1, 他们被转化为整数以后压入栈中, 栈变为[2,1]. 遇到+, 弹出两个操作数1和2(注意顺序), 计算2+1得到3
3.
from operator import add, sub, mul
op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)}
Python 中的 operator
模块提供了一组对应于 Python 内部操作符的高效函数。这些函数可以用于代替操作符,使代码更加简洁、可读性更强。
这段是定义了一个字典map, 用于将运算符+-*/映射到相应的运算函数上, 比如‘+’:add, 是将加法运算符‘+’映射到add函数上, 这里的add函数是指operator.add, 用于执行加法运算.
因为operator.truediv是一个浮点数除法, 返回一个浮点数. 例如truediv(5,2)返回的是2.5, 如果使用这个需要手动吧结果转换为整数int, 以确保结果是整数, 所以我们用
‘/': lambda x, y: int(x / y)
将除法运算符映射到一个匿名函数(lambda函数), 这个函数接受两个参数x和y, 并且返回x/y的整数部分, 是一个自定义除法
4. 整数除法仍然不符合逆波兰表达式(RPN)的定义, 在RPN中, 除法的结果应该是截断除法, 应该是向零方向取整, 而不是向下取整, 所以还要做一些调整
'/': lambda x, y: int(x / y) if x * y > 0 else -(-x // y)
为了确保正确处理,可以使用 //
操作符来实现截断除法,并在需要时进行符号调整。我们还可以对负数除法进行特别处理,以确保符合截断除法的定义。
5. 如果不想用operator, 还有一种办法, 就是在结尾用
int(eval(f'{second_num} {item} {first_num}')
eval函数会计算传入字符串表达式并返回结果, 如 eval('3+5')会返回整数‘8’
最后用int函数将eval返回的结果转换为整数形式
而eval函数所用的复杂度会更高