在python中用deque双向队列来表示栈或队列。或者用list来表示栈。
deque双向队列
常用函数如下:
python
from collections import deque
# 创建一个空的deque
d = deque()
# 创建一个包含元素的deque
d = deque([1, 2, 3])
# 在deque的右侧添加一个元素
d.append(4)
# 在deque的左侧添加一个元素
d.appendleft(0)
# 从deque的右侧移除并返回一个元素
d.pop()
# 从deque的左侧移除并返回一个元素
d.popleft()
# 扩展deque的右侧,通过添加iterable中的元素
d.extend([5, 6, 7])
# 扩展deque的左侧,通过添加iterable中的元素
d.extendleft([7, 6, 5])
# 向右旋转deque n步
d.rotate(2)
# 向左旋转deque n步
d.rotate(-2)
# 移除所有元素,使deque为空
d.clear()
# 返回deque中元素等于x的数量
d.count(1)
# 返回deque中第一个x元素的索引
d.index(2)
# 反转deque中的元素
d.reverse()
# 移除deque中第一个出现的value
d.remove(3)
# 获取指定索引的元素,支持负索引
first_element = d[0] # 获取第一个元素
last_element = d[-1] # 获取最后一个元素
# 设置指定索引的元素
d[0] = 100
# 删除指定索引的元素
del d[0]
leetcode20.有效的括号
python
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
stack = deque()
bracket_map = {')': '(', '}': '{', ']': '['}
for char in s:
# 当前遍历到右括号
if char in bracket_map:
# 栈不为空且栈顶元素与当前右括号匹配
if stack and stack[-1] == bracket_map[char]:
stack.pop()
else:
# 栈为空或不匹配
return False
# 当前遍历到左括号
else:
stack.append(char)
# 如果栈为空,说明所有括号都正确匹配
return not stack
效率:12ms,击败85.70%
leetcode1047.删除字符串中的所有相邻重复项
python
class Solution(object):
def removeDuplicates(self, s):
"""
:type s: str
:rtype: str
"""
stack = deque()
for char in s:
# 如果栈不为空且栈顶元素匹配
if stack and stack[-1] == char:
stack.pop()
else:
stack.append(char)
return ''.join(stack)
效率:90ms,击败14.25%,需要优化
优化
可以看到,如上版本的时间复杂度依旧是O(n),因为遍历一遍字符串需要O(n),然后加上额外的常数操作(弹出或加入元素),所以总体还是O(n)。如果想要优化的话,只能从数据结构的存储来优化,即舍弃栈,改为list。
python
class Solution(object):
def removeDuplicates(self, s):
"""
:type s: str
:rtype: str
"""
stack = list()
for char in s:
# 如果栈不为空且栈顶元素匹配
if stack and stack[-1] == char:
stack.pop(-1)
else:
stack.append(char)
return ''.join(stack)
55ms,击败68.83%
leetcode150.逆兰波表达式
注意使用lambda表达式简化if判断。
注意入栈时用int包裹,因为是字符串 。
而且python3对于除法是向0取整。而python2对于除法是向负无穷取整。所以python2的需要判断x和y是否同号,同号的话,需要计算x和y绝对值的除法再取反。
python(python2)
python
class Solution(object):
def evalRPN(self, tokens):
"""
:type tokens: List[str]
:rtype: int
"""
ops = {'+': lambda x, y: x + y, '-': lambda x, y: x - y, '*': lambda x, y: x * y, '/': lambda x, y: x / y if x * y > 0 else -(abs(x) / abs(y))}
stack = deque()
for token in tokens:
if token in ops:
y = stack.pop()
x = stack.pop()
res = ops[token](x, y)
# print("当前操作符:{}, 当前x:{}, 当前y:{}, 结果:{}".format(token, x, y, res))
stack.append(res)
else:
stack.append(int(token))
return stack.pop()
效率:25ms,击败70.08%
python3
python3
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
ops = {'+': lambda x, y: x + y, '-': lambda x, y: x - y, '*': lambda x, y: x * y, '/': lambda x, y: int(x / y)} # 注意除法后得到的是int
stack = deque()
for token in tokens:
if token in ops:
y = stack.pop()
x = stack.pop()
# print(f"当前操作符:{token}, 当前y:{y}, 当前x:{x}")
stack.append(ops[token](x, y))
else:
stack.append(int(token))
return stack.pop()
效率:40ms,击败76.51%
leetcode347.前k个高频元素
代码1:map+自定义排序
最先想到的是map(在python中的数据结构是dict)+自定义排序解决,如下
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
m = dict()
for num in nums:
m[num] = m.get(num, 0)+1
to_sort_list = [(key, val) for key,val in m.items()]
sorted_list = sorted(to_sort_list, key=lambda item:item[1], reverse=True)
top_k_keys = [item[0] for item in sorted_list[:k]]
return top_k_keys
效率:34ms,击败20.00%。需要优化。
代码2:小顶堆
代码1的时间复杂度为O(nlogn),主要来自于对整个list进行排序。但是我们只取前k大,所以其实只用找出前k大的元素即可,不用对整个list排序。为此想到元素为k个的小顶堆,或者说优先队列。这样时间复杂度是O(nlog k),当k远小于n时,优化明显。
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
# 使用 Counter 计算每个数字的频率
counter = Counter(nums)
# 使用 heapq.nlargest 来找到频率最高的 k 个元素
# 这里我们使用一个 lambda 函数作为 key,以便 heapq.nlargest 可以根据频率排序
top_k = heapq.nlargest(k, counter.items(), key=lambda item: item[1])
# 提取 top_k 中的元素(即数字本身)
return [item[0] for item in top_k]
效率:26ms,击败62.51%。还行,主要是代码简洁。