关于栈、队列和递归的几个必知必会的代码实现
栈
用数组实现一个顺序栈
用链表实现一个链式栈
编程模拟实现一个浏览器的前进、后退功能
队列
用数组实现一个顺序队列
用链表实现一个链式队列
实现一个循环队列
递归
编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
编程实现求阶乘 n!
编程实现一组数据集合的全排列
对应的 LeetCode 练习题
栈
Valid Parentheses(有效的括号)
LeetCode 20
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
解题思路
遇到左括号则入栈,遇到右括号则将其与栈顶元素对比,如果可以匹配,则出栈,否则返回 False
代码
class Solution:
def isValid(self, s: str) -> bool:
dic = {'(':')', '{':'}', '[':']', '?':'?'}
n = len(s)
res = ['?']
for i in range(n):
if s[i] in dic:
res.append(s[i])
elif dic[res.pop()] != s[i]:
return False
return len(res) == 1
复杂度分析
时间复杂度:
空间复杂度:
/Todo
Longest Valid Parentheses(最长有效的括号)
LeetCode32
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: “(()” 输出: 2
解释: 最长有效括号子串为 “()”
示例 2:
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
解题思路
左括号入栈,遇到右括号出栈,此时若栈为空,将当前标号压入;若不为空,当前标号与栈顶元素之差就是有效括号的长度。
代码
class Solution:
def longestValidParentheses(self, s: str) -> int:
stack = [-1]
length = 0
max_length = 0
for i in range(len(s)):
if s[i] == '(':
stack.append(i)
else:
stack.pop()
if stack == []:
stack.append(i)
else:
length = i - stack[-1]
max_length = max(max_length, length)
return max_length
复杂度分析
/Todo
Evaluate Reverse Polish Notatio(逆波兰表达式求值)
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入: [“2”, “1”, “+”, “3”, “*”]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) *3) = 9
示例 2:
输入: [“4”, “13”, “5”, “/”, “+”]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 /
5)) = 6
示例 3:
输入: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”,
“+”]
输出: 22
解释: 该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) +
17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。 该算式的逆波兰表达式写法为 ( ( 1 2 + ) (
3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
解题思路
遇到数字则入栈,遇到操作符则取栈顶两个元素进行运算后放入栈中
代码
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
res = []
ans = 0
n = len(tokens)
s = ['+', '-', '*', '/']
for c in tokens:
if c not in s:
#此处int(c)省了很多事,保证了res中全是整数了,可以直接进行运算
res.append(int(c))
elif c == '+':
x = res.pop()
res[-1] += x
elif c == '-':
x = res.pop()
res[-1] -= x
elif c == '*':
x = res.pop()
res[-1] *= x
else:
x = res.pop()
# 除法要求的是整数,只好int强制转换一下类型
res[-1] = int(res[-1] / x)
return res[-1]
复杂度分析
需要遍历整个列表,时间复杂度为O(n)
入栈需要空间,空间复杂度也为O(n)
队列
Design Circular Deque(设计一个双端队列)
设计实现双端队列。
你的实现需要支持以下操作:
MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。
示例:
MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1); // 返回 true
circularDeque.insertLast(2); // 返回 true
circularDeque.insertFront(3); // 返回 true
circularDeque.insertFront(4); // 已经满了,返回 false
circularDeque.getRear(); // 返回 2 circularDeque.isFull();
// 返回 true circularDeque.deleteLast(); // 返回 true
circularDeque.insertFront(4); // 返回 true
circularDeque.getFront(); // 返回 4
解题思路
熟悉一下怎么设计一个双端队列
代码
class MyCircularDeque:
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the deque to be k.
"""
self.max_len = k
self.queue = []
def insertFront(self, value: int) -> bool:
"""
Adds an item at the front of Deque. Return true if the operation is successful.
"""
if len(self.queue) < self.max_len:
self.queue = self.queue[::-1]
self.queue.append(value)
self.queue = self.queue[::-1]
return True
return False
def insertLast(self, value: int) -> bool:
"""
Adds an item at the rear of Deque. Return true if the operation is successful.
"""
if len(self.queue) < self.max_len:
self.queue.append(value)
return True
return False
def deleteFront(self) -> bool:
"""
Deletes an item from the front of Deque. Return true if the operation is successful.
"""
if self.queue:
del self.queue[0]
return True
return False
def deleteLast(self) -> bool:
"""
Deletes an item from the rear of Deque. Return true if the operation is successful.
"""
if self.queue:
del self.queue[-1]
return True
return False
def getFront(self) -> int:
"""
Get the front item from the deque.
"""
if self.queue:
return self.queue[0]
return -1
def getRear(self) -> int:
"""
Get the last item from the deque.
"""
if self.queue:
return self.queue[-1]
return -1
def isEmpty(self) -> bool:
"""
Checks whether the circular deque is empty or not.
"""
if len(self.queue) == 0:
return True
return False
def isFull(self) -> bool:
"""
Checks whether the circular deque is full or not.
"""
if len(self.queue) == self.max_len:
return True
return False
复杂度分析
/Todo
Sliding Window Maximum(滑动窗口最大值)
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
进阶:
你能在线性时间复杂度内解决此题吗?
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7]
解释:
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4 1 <= k <= nums.length
解题思路
有关滑动窗口问题,可以看一下这篇文章:我写了套框架,把滑动窗口算法变成了默写题
代码
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
deque = collections.deque()
res = []
for i, num in enumerate(nums):
while deque and deque[0] <= i - k:
deque.popleft()
while deque and num > nums[deque[-1]]:
deque.pop()
deque.append(i)
if i >= k - 1:
res.append(nums[deque[0]])
return res
复杂度分析
/Todo
递归
Climbing Stairs(爬楼梯)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
解题思路
直接递归,加个缓存装饰器就不会出现超时问题
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n) = f(n-1) + f(n-2)
f(n)=f(n−1)+f(n−2)
代码
class Solution:
@functools.lru_cache(100)
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
if n == 2:
return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
复杂度分析
/Todo