一、前言
因为栈和队列在平常代码中没有列表或者字符串等熟练,但是这类数据结构在力扣刷题中有不小的体量,所以这里对两者做一个总结,主要包括了结构的理解、常用的方法和一些经典的题目。这里同样也是第一版,后续有时间再慢慢完善。
二、栈和队列的概念
这里对于栈和队列的内容,主要是在【代码随想录:栈和队列】中进行学习,并按照自己的理解撰写。这里不对原理做详细介绍,想了解可以查看原文。
1、什么是栈
可以将栈和队列理解成一个只有一个出口的容器,同时,栈遵从数据的先进后出,而队列遵从数据的先进先出,他们都没办法通过索引直接获取容器中间的元素。
在python中,对于两者的实现一般都可以用list完成。
# 栈的进出
stack = []
stack.append('a')
stack.pop()
# 队列的进出
stack.append('a')
stack.pop(0)
如果是为了追求更好的性能,python也提供了collections.deque()这个数据结构。deque是栈和队列的一种广义实现,是一种双向队列;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。
import collections
deque = collections.deque()
# deque的入栈和出栈
deque.append() # 只压入一个元素
deque.appendleft()
deque.expend() # 压入多个元素
deque.expendleft()
deque.pop() # 弹出栈顶元素
deque.popleft()
二、常见例题
1、有效符号(一般是括号这种成对的符号)
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
3.每个右括号都有一个对应的相同类型的左括号。
这类题目的思路可以使用栈,原理是按照顺序将元素压入栈,如果左右符号匹配,则进行相关操作,否则继续。
法一:
def isValid(self, s: str) -> bool:
stack = []
for ch in s:
if ch == '{':
stack.append('}')
elif ch == '[':
stack.append(']')
elif ch == '(':
stack.append(')')
elif not stack or stack[-1] != ch:
return False
else:
stack.pop()
return True if not stack else False
法二:
class Solution:
def isValid(self, s: str) -> bool:
dic = {'{': '}', '[': ']', '(': ')', '?': '?'}
stack = ['?']
# 这里为了防止pop一个空的栈
for c in s:
if c in dic: stack.append(c)
elif dic[stack.pop()] != c: return False
return len(stack) == 1
【32.最长有效括号】
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号
子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
本题的思路在于找到最后一个不匹配的 ‘)’ ,并记录对应的下标,这些下标将整个字符串分割成多个子串,每个子串都是一个完整有效的括号。
class Solution:
def longestValidParentheses(self, s):
if len(s) == 0 or len(s) == 1:
return 0
stack = [] #
j = 0
max_len = 0
while j < len(s):
if s[j] == "(":
stack.append(j)
if s[j] == ")":
if len(stack) and s[stack[-1]] == "(":
stack.pop()
if len(stack):
lens = j - stack[-1]
if lens > max_len:
max_len = lens
else:
lens = j + 1
if lens > max_len:
max_len = lens
else:
stack.append(j)
j += 1
return max_len
2、单调队列
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
本题目的思考在于如何在不超时的情况下找到子数组的最大值,如果直接通过队列每次进出一个元素,在剩下的队列中直接调用Max()函数会导致超时,所以这里需要调用单调队列的方法。
什么是单调队列?这同样是一种队列,但是队列要求从队列出口到入口的元素是从大到小排列的,那么我们要找到最大值的话,直接从出口弹出一个元素就可以了。
但是单调队列的入队和出队方法需要重新定义:
class mydeque():
def __init__(self):
self.deque = collections.deque()
def pop(self, value):
# 比较当前要弹出来的元素是否是站顶元素,如果是的话才会弹出
if self.deque and self.deque[0] == value:
self.deque.popleft()
def push(self, value):
while self.deque and self.deque[-1] < value:
self.deque.pop()
self.deque.append(value)
def front(self):
return self.deque[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
stack = mydeque()
res = []
for i in range(k):
stack.push(nums[i])
res.append(stack.front())
for i in range(k,len(nums)):
stack.pop(nums[i - k])
stack.push(nums[i])
res.append(stack.front())
return res