今日任务:
1)20. 有效的括号
2)1047. 删除字符串中的所有相邻重复项
3)150. 逆波兰表达式求值
4)239. 滑动窗口最大值
20. 有效的括号
题目链接:20. 有效的括号 - 力扣(LeetCode)
文字及视频讲解:代码随想录 (programmercarl.com)
思路:
由于栈先进后出的结构,我们可以使用栈,这样就可以做到括号的前后匹配。
首先要弄清楚,字符串里的括号不匹配有几种情况。
1)字符串里左方向的括号多余了:
解决办法:发现已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
2)括号没有多余,但是 括号的类型没有匹配上:
解决办法:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false
3)字符串里右方向的括号多余了:
解决办法:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
# 使用list
stack = []
for item in s:
if item =='(':
stack.append(')')
elif item =='{':
stack.append('}')
elif item =='[':
stack.append(']')
elif not stack or stack[-1] != item:
return False
else:
stack.pop()
if not stack:
return True
else:
return False
感想:
由于栈结构的特殊性,非常适合做对称匹配类的题目。
1047. 删除字符串中的所有相邻重复项
题目链接:1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)
文字及视频讲解:代码随想录 (programmercarl.com)
思路:
这也是匹配问题,20. 有效的括号 是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。
方法一:使用栈
方法二:双指针
方法一:使用栈
class Solution(object):
def removeDuplicates(self, s):
"""
:type s: str
:rtype: str
"""
stack = list()
for item in s:
if stack and stack[-1] == item:
stack.pop()
else:
stack.append(item)
return ''.join(stack)
方法二:双指针
class Solution:
def removeDuplicates(self, s: str) -> str:
res = list(s)
slow = fast = 0
length = len(res)
while fast < length:
# 如果一样直接换,不一样会把后面的填在slow的位置
res[slow] = res[fast]
# 如果发现和前一个一样,就退一格指针
if slow > 0 and res[slow] == res[slow - 1]:
slow -= 1
else:
slow += 1
fast += 1
return ''.join(res[0: slow])
感想:
匹配问题优先使用栈,看看是否可以解决问题。
150. 逆波兰表达式求值
题目链接:150. 逆波兰表达式求值 - 力扣(LeetCode)
文字及视频讲解:代码随想录 (programmercarl.com)
思路:
首先先了解一下什么是逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。
所以适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。
class Solution(object):
def evalRPN(self, tokens):
"""
:type tokens: List[str]
:rtype: int
"""
stack = list()
for item in tokens:
if item not in {'+', '-', '*' ,'/'}:
stack.append(int(item))
else:
num1 = stack.pop()
num2 = stack.pop()
if item == '+':
result = num1 + num2
elif item == '-':
result = num2 - num1 # 注意减法的顺序
elif item == '*':
result = num1 * num2
elif item == '/':
if num1 *num2 >0:
result = num2 // num1
else:
result = -(abs(num2)//abs(num1))
stack.append(result)
return int(stack.pop())
感想:
思路不难,但是要注意一个小点,就是在遇到除法的时候,需要判断是否存在只有一个是负数的情况,因为整除函数会向下取整到最接近的整数,而当n1
是负数时,向下取整意味着结果将是更小的整数。
239. 滑动窗口最大值
题目链接:239. 滑动窗口最大值 - 力扣(LeetCode)
文字及视频讲解:代码随想录 (programmercarl.com)
思路:
方法一:(暴力法)
直接采用两层循环遍历数组,每次取出滑动窗口中最大值,提交超时,时间复杂度为O(kn)
方法二:(采用单调队列)
1)首先,我们需要自己实现单调队列,队列中只维护最大值即可。
2)怎么实现呢?
对于push函数:当进来一个数时,比较其与队列中最后一个数的大小;若新增数大,则在弹出队列中的数。若队列中的数大,则直接在队列后面添加新增数。
第一次遍历k个元素后,现在需要将队列进行前移,此时我们要将需要将最前面的元素弹出,同时新增一个数,重复2)过程
对于pop函数:由于push的时候第一个数只保存为最大的元素,因此比较小的数早就被弹出去了,所以此时只需要判断队列第一个数是否等于此时列表k的第一个数,等于的话就说明需要pop出这个最大的值了。
方法一:(暴力法)
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
left = 0
max_result = float('-inf')
result = []
while left <= len(nums) - k:
list1 = []
for i in range(k):
list1.append(nums[i+left])
left += 1
result.append(max(list1))
return result
方法二:(单调队列法)
from collections import deque
class myquene: #单调队列(从大到小
def __init__(self):
self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
#这样就保持了队列里的数值是单调从大到小的了。
def push(self,value):
while self.queue and self.queue[-1]<value:
self.queue.pop() # 注意一下这是pop
self.queue.append(value)
#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
#同时pop之前判断队列当前是否为空。
def pop(self,value):
if self.queue and self.queue[0] == value:
self.queue.popleft() # 注意一下这是popleft
#查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
def get_max(self):
return self.queue[0]
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
result = []
left = 0
queue = myquene()
for i in range(k): #先将前k的元素放进队列
queue.push(nums[i])
result.append(queue.get_max()) #result 记录前k的元素的最大值
while left < len(nums) - k:
queue.pop(nums[left]) #滑动窗口移除最前面元素
queue.push(nums[left + k ]) #滑动窗口前加入最后面的元素
result.append(queue.get_max()) #记录对应的最大值
left += 1
return result
感想:
这题核心是要用单调队列结构。注意一下pop的左右顺序!