开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情
作者: 千石
支持:点赞、收藏、评论
欢迎各位在评论区交流
本文大纲:
前言
题目练习步骤:
- 给自己10分钟,读题并思考解题思路
- 有了思路以后开始写代码,如果在上一步骤中没有思路则停止思考并且看该题题解
- 在看懂题解(暂时没看懂也没关系)的思路后,背诵默写题解,直至能熟练写出来
- 隔一段时间,再次尝试写这道题目
一些解题思路
栈:
栈是一种后进先出的数据结构,常用于模拟系统的调用栈,以及简化递归问题的求解。
在解题中,栈可以用于模拟基于栈的算法,例如括号匹配、表达式求值等。
同时,栈也可以在求解回溯问题(例如八皇后问题)时作为辅助数据结构。
队列:
队列是一种先进先出的数据结构,常用于模拟系统中的队列,例如打印队列等。
在解题中,队列可以用于模拟广度优先搜索(BFS),以及维护一些具有先后顺序的任务,例如动态规划问题的求解。
优先队列:
优先队列是一种具有优先级的队列,在出队时,具有最高优先级的数据会优先出队。
在解题中,优先队列常常被用于解决最短路径问题(例如Dijkstra算法)和动态选择问题(例如贪心算法)。
实战
题目一:20. 有效的括号
解题思路
- 暴力
不断地使用 python 内置的 replace 函数来匹配括号,直到不再有任何括号为止
1. 初始化一个字符串,用于存储有效的括号序列。
2. 使用 replace 函数,将匹配的括号对替换为空字符串。
3. 重复步骤 2,直到不再有任何括号为止。
4. 最后,判断字符串是否为空,若为空,则说明括号序列有效;否则,说明括号序列无效。
复杂度分析:
- 时间复杂度:
- 每次执行 replace 函数的时间复杂度为 ,其中 为字符串的长度。因此,最坏情况下,整个算法的时间复杂度为 。
- 空间复杂度:
- 每次执行 replace 函数,都会创建一个新字符串,因此空间复杂度为 。
执行结果:
- 栈
从左到右扫描字符串,并在栈中模拟括号的匹配过程。 1. 创建一个栈,用于存储遇到的左括号。 2. 遍历字符串中的每个字符,如果是左括号,则将其压入栈中。如果是右括号,则从栈中弹出一个左括号,并判断它们是否匹配。 3. 如果在遍历完整个字符串之后,栈仍有元素,则说明括号序列无效。 4. 反之,如果栈为空,说明括号序列有效。 复杂度分析:
- 时间复杂度:
- 在最坏情况下,每个括号都要入栈和出栈一次,因此整个算法的时间复杂度为 ,其中 为字符串的长度。
- 空间复杂度:
- 空间复杂度为 ,其中 为字符串的长度。因为栈只需要存储最多 个元素。
执行结果:
代码实现
- 暴力
def isValid(s: str) -> bool:
while '()' in s or '[]' in s or '{}' in s:
s = s.replace('()', '').replace('[]', '').replace('{}', '')
return not s
- 栈
def isValid(s: str) -> bool:
stack = []
mapping = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in mapping:
if not stack or stack.pop() != mapping[char]:
return False
else:
stack.append(char)
return not stack
题目二:84. 柱状图中最大的矩形
解题思路
- 暴力(会超时)
对于每一列柱子,向两边扩展,直到遇到比当前柱子低的柱子为止,计算以该柱子为高的矩形面积,并在所有柱子中取最大值。
复杂度分析:
-
时间复杂度:
- 对于每个柱子,需要向两边扩展,复杂度为O(n^2)。
-
空间复杂度:
- 只需要常数空间,复杂度为O(1)。
思路应该没问题,不过超时了
- 优化的暴力
从左到右依次枚举每个柱子,计算它作为矩形最短边向左和右扩展的最大长度,与它的高度相乘即可得到最大面积。
如果每个柱子都这样计算一遍,时间复杂度为 ,可以通过使用一个单调栈来优化,使得时间复杂度为 。
复杂度分析:
- 时间复杂度:
- 空间复杂度:
执行结果:
- 栈
栈可以帮助维护从栈底到栈顶的高度单调递增的柱子序列,当前柱子高度不断递减时,弹出栈顶元素,并计算以该柱子为高的矩形面积,最后栈中元素仍未弹出时计算面积
复杂度分析:
-
时间复杂度:
- 对于每个柱子,只会被压入栈一次且弹出一次,复杂度为O(n)。
-
空间复杂度:
- 需要一个栈维护高度单调递增的柱子序列,复杂度为O(n)。
执行结果:
代码实现
- 暴力(会超时)
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
max_area = 0
for i in range(n):
left = i
right = i
while left > 0 and heights[left - 1] >= heights[i]:
left -= 1
while right < n - 1 and heights[right + 1] >= heights[i]:
right += 1
max_area = max(max_area, (right - left + 1) * heights[i])
return max_area
- 优化的暴力
def largestRectangleArea(heights):
n = len(heights)
left, right = [0] * n, [0] * n
mono_stack = []
for i in range(n):
while mono_stack and heights[mono_stack[-1]] >= heights[i]:
mono_stack.pop()
if not mono_stack:
left[i] = 0
else:
left[i] = mono_stack[-1] + 1
mono_stack.append(i)
mono_stack = []
for i in range(n - 1, -1, -1):
while mono_stack and heights[mono_stack[-1]] >= heights[i]:
mono_stack.pop()
if not mono_stack:
right[i] = n - 1
else:
right[i] = mono_stack[-1] - 1
mono_stack.append(i)
res = 0
for i in range(n):
res = max(res, (right[i] - left[i] + 1) * heights[i])
return res
- 栈
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
stack = [-1]
max_area = 0
for i in range(n):
while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:
height = heights[stack.pop()]
width = i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
while stack[-1] != -1:
height = heights[stack.pop()]
width = n - stack[-1] - 1
max_area = max(max_area, height * width)
return max_area
总结
本文的主要内容是简单总结了栈、队列和优先队列在解决算法题目中的应用技巧,同时给出了这些数据结构在解决一些算法题目时的思路和代码实现。