2022 1 月
20220102
动态规划解题套路框架
完成
509. 斐波那契数(简单)
322. 零钱兑换(中等)
20220103
回溯算法解题套路框架
使用回溯法完成 n 皇后
51. N皇后(困难)
class Solution:def solveNQueens(self, n: int) -> List[List[str]]:board = [["." for j in range(n)]for i in range(n)]sit = 0ans = []def valid( i , j):ti, tj = i, jwhile 0 <= ti < n and 0 <= tj < n:if board[ti][tj] == "Q":return Falseti -= 1tj -= 1ti, tj = i, jwhile 0 <= ti < n and 0 <= tj < n:if board[ti][tj] == "Q":return Falseti -= 1ti, tj = i, jwhile 0 <= ti < n and 0 <= tj < n:if board[ti][tj] == "Q":return Falseti -= 1tj += 1return Truedef dfs(n_sit, row):# print(f"----------{n_sit} {row}")if n_sit == n:# 摆完一轮了# print(board)ans.append(["".join(line) for line in board])# exit()returni = rowfor j in range(n):if valid(i, j):# print(f"choose {i} {j}")board[i][j] = "Q"# print(board)dfs(n_sit + 1, row + 1)# breakboard[i][j] = "."# print(f"remove {i} {j}")# print(board)dfs(sit, 0)return ans
20220104
BFS 算法解题套路框架
BFS和DFS相比,BFS空间复杂度高,以满二叉树为例子,最高时O(n)的空间复杂度,DFS最多时O(logn)的空间复杂度。
111. 二叉树的最小深度(简单)
# Definition for a binary tree node.# class TreeNode:# def __init__(self, val=0, left=None, right=None):# self.val = val# self.left = left# self.right = rightclass Solution:def minDepth(self, root: TreeNode) -> int:ans = 0que = collections.deque()que.append(root)if not root:return 0while que:ans += 1n = len(que)# print(que)# print(n)for i in range(n):node = que.popleft()if not node.left and not node.right:return ansif node.left:que.append(node.left)if node.right:que.append(node.right)return ans
20220105
752.打开转盘锁
bfs 结合备忘录,必须有备忘录,否则会重复搜索元素陷入死循环。
时间复杂度:最多存储 10000次个节点
空间复杂度:最多搜索 10000次,
class Solution:def openLock(self, deadends: List[str], target: str) -> int:que = collections.deque()que.append("0000")ans = 0visit = [0] * 10000visit[0] = 1while que:n = len(que)# 每次取出队列中的所有元素,每次队列中的元素代表一步操作内的所有状态for i in range(n):node = que.popleft()# 不能出现的序列跳过if node in deadends:continue# 结束条件if node == target:return ans# 每拿到一个元素,可以逐个字符旋转,形成8个新的状态for i in range(4):up, down = self.next(node[i])# print(f"---------{i} {up} {down}")if i == 3:new = node[:i] + upif visit[int(new)] == 0:que.append(new)visit[int(new)] = 1new = node[:i] + downif visit[int(new)] == 0:que.append(new)visit[int(new)] = 1else:new = node[:i] + up + node[i + 1:]if visit[int(new)] == 0:que.append(new)visit[int(new)] = 1new = node[:i] + down + node[i + 1:]if visit[int(new)] == 0:que.append(new)visit[int(new)] = 1ans += 1# if ans == 2:# breakreturn -1def next(self, s):s_up = (int(s) + 1 ) % 10s_down = (10 + int(s) - 1 ) % 10return [str(s_up), str(s_down)]
BFS 算法框架
def BFS(target):que = collections.deque()que.append("start")visit = [0] * 10000visit[0] = 1while que:n = len(que)for i in range(n):node = que.popleft()# 遍历下层元素,向四周扩散for i in range(4):new = node + i# 结束条件if new == target:return ansque.append(new)ans += 1return
567.字符串的排列
- 解题思路
维持滑动窗口记录当前的匹配串,s2中出现s1的排列这个问题可以转化为,在滑动窗口中可以使用哪些字符以及这些字符可以使用几次,一个字典记录s1中字符出现次数。
然后遍历一遍s2,cnt 记录成功匹配的个数。
时间复杂度:O(n)
空间复杂度:O(n)
- 代码
class Solution:def checkInclusion(self, s1: str, s2: str) -> bool:l = 0r = -1n1 = len(s1)dic = dict()for i in range(n1):if s1[i] in dic.keys():dic[s1[i]] += 1else:dic.setdefault(s1[i], 1)cnt = 0n = len(s2)while r < n - 1:r += 1# 出现不可用字符,直接舍弃之前的匹配if s2[r] not in dic.keys():while s2[l] != s2[r]:dic[s2[l]] += 1cnt -= 1l += 1l += 1continue# 出现可用字符但次数不够,则舍弃左侧字符直到出现一个可用字符if dic[s2[r]] == 0:while s2[l] != s2[r]:dic[s2[l]] += 1cnt -= 1l += 1l += 1# 正常匹配一个字符if dic[s2[r]] != 0:cnt += 1dic[s2[r]] -= 1# 结束条件if cnt == n1:return Truereturn False
20220106
Dijkstra 算法可以理解为BFS+DP table+优先队列
20220107
743. 网络延迟时间
- 解题思路
根据输入建图,从起始点开始使用BFS进行层序遍历,遍历时使用优先队列存储元素,同时使用dp table存储源节点到每个节点的距离。
需要注意入队列时要把距离放在元素的第一位。
时间复杂度:O(n)
空间复杂度:O(n) - 代码
class Solution:def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:import heapq# 建图gra = dict()for (src, dst, val) in times:if src in gra.keys():gra[src].append((dst, val))else:gra.setdefault(src, [(dst, val)])if k not in gra.keys():return -1# 初始化dp tabletable = [float('inf')] * (n + 1)table[0] = -1table[k] = 0que = [(0, k)]while que:# 优先队列pop距离最小的点cur_val, cur_node = heapq.heappop(que)if cur_node not in gra.keys():continuefor next_node, next_val in gra[cur_node]:next_val += cur_valif next_val < table[next_node]:table[next_node] = next_valheapq.heappush(que, (next_val, next_node))ans = max(table)return ans if ans < float('inf') else -1
python 优先队列的使用
import heapqque = []heapq.heappush(que, (1, 10))heapq.heappush(que, (2, 9))heapq.heappush(que, (2, 8))heapq.heappush(que, (3, 8))print(heapq.heappop(que))print(heapq.heappop(que))print(heapq.heappop(que))print(heapq.heappop(que))# 输出如下,因此我们可以知道元素排序是默认从第一个元素开始比较的。(1, 10)(2, 8)(2, 9)(3, 8)
Dijkstra
- todo
1514. 概率最大的路径
- 解题思路
使用dijstra 计算src 到 dst 的最小路径,需要注意的是由于这题是无向图,所以建图的时候要双向都要存储。
时间复杂度:o(n)
空间复杂度:O(n) - 代码
class Solution:def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:import heapq# 建图gra = dict()for i, (src, dst) in enumerate(edges):if src not in gra.keys():gra.setdefault(src, [(succProb[i], dst)])else:gra[src].append((succProb[i], dst))if dst not in gra.keys():gra.setdefault(dst, [(succProb[i], src)])else:gra[dst].append((succProb[i], src))# 初始化队列和tableque = [(0, start)]table = [0] * ntable[start] = 0while que:# 因为heappo默认弹出最小值,所以存的时候 1-概率,保证弹出的概率值最大。cur_p_sub, cur_node = heapq.heappop(que)cur_p = 1 - cur_p_subif cur_node == end:return cur_pif cur_node not in gra.keys():continuefor next_p, next_node in gra[cur_node]:next_p *= cur_pif next_p > table[next_node]:table[next_node] = next_pheapq.heappush(que, (1 - next_p, next_node))return 0.0
