给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
可以先模拟一下匹配的过程,每当有一个左括号出现时,有效的字符串中在后面肯定会有一个对应的右括号,因此我们可以用栈来实现这个过程。
对字符串进行遍历操作,每遍历到一个左括号,就在栈中添加一个对应的右括号进去,每遇到一个右括号,就与栈中的顶部元素相比较,如果相同就弹出;不相同肯定就是无效的括号了。因为括号的使用肯定是不能跨级的,比如不能出现([)]
这种情况,因此可以直接认定为无效。故代码如下:
class Solution:
def isValid(self, s: str) -> bool:
stack = [] #初始化一个列表来实现栈
if len(s) == 1:
return False
for char in s:
if char == '(':
stack.append(')')
elif char == '[':
stack.append(']')
elif char == '{':
stack.append('}')
elif not stack or stack[-1] != char:
return False
elif stack and stack[-1] == char:
stack.pop()
if not stack:
return True
else :
return False
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
这道题其实类似于上面那道,遇到这种需要匹配删除操作的可以第一时间考虑栈。思路为:遍历字符串,如果遇到与栈顶部元素相同的字符,则直接弹出;若不相同,则压入栈中。代码如下:
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for char in s:
if stack and char == stack[-1]:
stack.pop()
elif not stack:
stack.append(char)
elif stack and char != stack[-1]:
stack.append(char)
return ''.join(stack)
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
有效的算符为 '+'
、'-'
、'*'
和 '/'
。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。
这道题有点难度,大一的时候也了解过逆波兰表达式,但当时就是稀里糊涂的写过了,完全没有理解到位,这个题的思路为:遇到数字时压入栈中备用,然后遇到运算符时就弹出两个数字进行相应的运算,再将运算结果压入栈中。
有一个点需要注意,这里的除法总是 向零截断 ,这是什么意思呢?就是在式子中有负数的情况下,例如-7除以3,此时得出来的结果为-2,而整除时得出来的结果为-3,所以这道题目不能使用整除来处理,只能老老实实的用int()提取整数部分。代码如下:
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack_num = [] #用于存储数字
for item in tokens:
if item not in {"+", "-", "*", "/"}:
stack_num.append(int(item))
else:
a = stack_num.pop()
b = stack_num.pop()
if item == '+':
stack_num.append(a+b)
elif item == '-':
stack_num.append(b-a)
elif item == '*':
stack_num.append(b*a)
elif item == '/':
stack_num.append(int(b/a))
return stack_num[0]
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
这个题本想着用切片做的,结果一试,超时了,想了想确实,暴力求解的话时间复杂度到了n*m了。那就得换个思路,能不能只遍历一次,而且求最大值的时候也不需要遍历地求出来呢?
滑动窗口有没有很像移动的队列?我们可以利用队列对滑动窗口中的数值求最大值。那具体怎么求最大值呢?总不能像暴力法一样用max吧,这样时间复杂度又变成一样了,题目本质上是要求我们求出来滑动窗口的最大值,对其他的数值没有要求,因此,我们可以去除掉其他不相关数,只保留滑动窗口中的最大值。而且这个最大值要在队列中容易读出,那肯定就要安排到队列头了。
具体怎么实现呢?
要把每个窗口的最大值安排到队列头中,就要在压入数据时先判断该数据是否大于队列中的其他数据,如果大于,则把队列中的其他数据全部去除;如果小于,则直接压入队列即可。
那如果最大值正好是要弹出的窗口值怎么办?
这时候直接弹出对应的值即可。
现在的思路就捋顺了。先创建一个队列,然后将前k个值按照push函数的思路存到队列中,此时第一个最大值就已经有了。然后开始在循环中移动滑动窗口,因为pop函数的实现是如果有对应的值,我们就弹出,没有就忽略,所以在移动窗口的过程中,我们可以放心大胆的pop出窗口的第一个数据;再压入新的数据,每次循环中都保留每次循环得到的最大值,因为每次循环都对应着一个窗口。
因此,代码如下:
class MyQueue:
def __init__(self):
self.items = [] #队列初始化
def pop(self,value):
if self.items and self.items[0] == value:
self.items.pop(0)
def push(self,x):
while self.items and x > self.items[-1]:
self.items.pop()
self.items.append(x)
def getMax(self):
return self.items[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
ans = [] #存储最大值
que = MyQueue()
for i in range(k): #先将第一个窗口压入队列
que.push(nums[i])
ans.append(que.getMax())
for i in range(k, len(nums)):
que.pop(nums[i - k]) #弹出窗口的第一个元素
que.push(nums[i]) #压入新窗口的最后一个元素
ans.append(que.getMax())
return ans
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
这个题我第一时间想到的思路是采用字典分别存储元素及其出现的频率,然后将字典中的值按照从大到小进行排序,最后输出对应的k个键。代码如下:
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
dic = {}
for num in nums:
dic[num] = dic.get(num, 0) + 1
sorted_dic = dict(sorted(dic.items(), key=lambda item: -item[1]))
return list(sorted_dic.keys())[:k]
key=lambda item: -item[1])
这段代码是指定了按照从大到小的顺序将字典的值进行排序。
list(sorted_dic.keys())[:k]
这段代码则是将排序后的字典的键值转化为列表,然后输出其前k个键。
但是这样的时间复杂度就为O(nlogn)
了。