主要是给自己方便复习的,
b站链接比较多哈哈哈。
不是很全面也有点杂乱,今后会陆续更新修改,补全知识点,见谅哈。
目录
一、模板
1.基础模板
2.DFS(递归回溯)常见模板
二、BFS例题
1.迷宫
三、DFS(递归)例题
1.N皇后问题
代码实现(详细过程注释)
2.全排列问题
题目描述
代码实现
3.不同路径数
题目描述
代码实现
4. 跳跃
思路分析
代码实现
一、模板
[Python] BFS和DFS算法(第1讲)_哔哩哔哩_bilibili
1.基础模板
# 字典初始化图
graph = {
'A': ['B', 'C'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B', 'D', 'E'],
'D': ['B', 'C', 'E', 'F'],
'E': ['C', 'D'],
'F': ['D']
}
# 广度优先搜索 breadth(一层一层搜索)
def BFS(graph, s): # 图,第一个开始的节点(start)
queue = [s] # 队列
seen = set(s) # 记录已经访问过的节点(用集合可以自动去除重复节点)
parent = {s: None}
while queue:
vertex = queue.pop(0) # 顶点(弹出队列的第一个元素)
nodes = graph[vertex] # vertex的相邻节点 如A的相邻节点为B,C
for w in nodes:
if w not in seen: # 如果该相邻节点没有被访问
queue.append(w)
parent[w] = vertex
seen.add(w)
# print(vertex, end=' ')
return parent
# 深度优先搜索(栈) depth(一条路走到黑,走不动了往回走)
def DFS(graph, s):
stack = [s] # 栈
seen = set(s) # 记录已经访问过的节点
while stack:
vertex = stack.pop() # 顶点(弹出栈的最后一个元素)
nodes = graph[vertex] # vertex的相邻节点 如A的相邻节点为B,C
for w in nodes: # 这里可以理解为随机放一个数进栈(注意放的是先进的元素,但整个搜索是沿着后进的元素的)
if w not in seen: # 如果该相邻节点没有被访问
stack.append(w)
seen.add(w)
print(vertex, end=' ')
# 深度优先搜索(递归回溯法)(灰常重要)
'''
详见下
'''
parent = BFS(graph, 'E')
# 从F出发走到E(起点)的最短路径
v = 'F'
while v is not None:
print(v)
v = parent[v]
print("\n--------")
DFS(graph, 'A')
2.DFS(递归回溯)常见模板
例题:玩具蛇
(该题我在写dfs函数时,省略了一个参数:step,即当前蛇走到的步数,一般题目中是需要这个参数进行辅助的)
- 思路一
对(i, j)的上下左右的坐标进行操作:
对(i, j)的上下左右的坐标(x,y)进行操作,若(x,y)满足条件,则递归(i,j)的上下左右4个点
lst = [[0] * 4 for _ in range(4)]
res = 0
def dfs(i, j):
global res
if lst[i][j] == 16: # 到了目的地
res += 1 # 方法 + 1
return # 返回(结束当前dfs函数)
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: # 遍历(i,j)的上下左右4个点(x,y),然后进行操作
x = i + dx
y = j + dy
if 0 <= x < 4 and 0 <= y < 4: # 如果(x,y)在范围内
if lst[x][y] == 0: # 并且蛇没游到这个点
lst[x][y] = lst[i][j] + 1 # 记录当前点为第几步
dfs(x, y) # 递归,查找当前点的上下左右4个点
lst[x][y] = 0 # 回溯,即返回上一步
# 如果4个点都访问过,结束当前dfs函数
for i in range(4):
for j in range(4):
lst[i][j] = 1 # 起点记录为第一步
dfs(i, j)
lst[i][j] = 0 # 递归结束后,对起点也进行回溯(最上面的dfs函数无法使起点回溯)
print(res)
- 思路二
对当前(i,j)点进行操作:
√ 对当前(i,j)点进行操作如果(i,j)满足条件,再递归(i,j)的上下左右4个点
lst = [[0] * 4 for _ in range(4)]
res = 0
def dfs(i, j, step):
global res
if step == 16: # 到了目的地
res += 1 # 方法 + 1
return # 返回(结束当前dfs函数)
if lst[i][j] == 0:
lst[i][j] = step
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: # 遍历(i,j)的上下左右4个点(x,y),然后进行操作
x = i + dx
y = j + dy
if 0 <= x < 4 and 0 <= y < 4: # 如果(x,y)在范围内
if lst[x][y] == 0: # 并且蛇没游到这个点
dfs(x, y, step + 1) # 递归,查找当前点的上下左右4个点
lst[i][j] = 0 # 函数结束时回溯,将该点回收
for i in range(4):
for j in range(4):
dfs(i, j, 1)
print(res)
DFS深搜与BFS广搜 C++代码详解_哔哩哔哩_bilibili
↑DFS、BFS(栈)以及BFS(递归)之间的细节与区别
DFS深搜与BFS广搜 C++代码详解_哔哩哔哩_bilibili
二、BFS例题
1.迷宫
data = '''01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000'''
lst = []
for d in data.split():
lst.append(list(d))
# 一旦有到达终点的就返回,这样就保证了最短路径(多个分路同时进行,最先到达终点的直接返回结果)
def bfs(i, j):
queue = [(i, j, '')]
seen = set()
while queue:
x, y, m = queue.pop(0)
if 0 <= x < len(lst) and 0 <= y < len(lst[0]) and lst[x][y] == '0' and (x, y) not in seen:
seen.add((x, y))
queue.append((x + 1, y, m + 'D')) # 保证字典序由小到大进行搜索,那么最后最先到达终点的就是字典序最小的
queue.append((x, y - 1, m + 'L'))
queue.append((x, y + 1, m + 'R'))
queue.append((x - 1, y, m + 'U'))
if x == len(lst) - 1 and y == len(lst[0]) - 1:
return m
print(bfs(0, 0))
三、DFS(递归)例题
1.N皇后问题
代码实现(详细过程注释)
n = int(input())
# 因为每行只放置1个皇后,所以数组可以这么表示↓,这样就可以根据行数快速找到皇后的位置
a = [0] * 10 # a[i]表示第i行上的皇后放于第a[i]列上
cnt = 0
def check(x, y): # 判断当前皇后能不能放到第x行第y列上
# 不能处于同一行 这个不需要检查,因为我们是一行一行按顺序放置皇后的
for i in range(1, x): # 枚举前面放置了皇后的行
if a[i] == y: # 不能处于同一列
return False
if i + a[i] == x + y: # 不能处于同一斜线(/)
return False
if i - a[i] == x - y: # 不能处于同一斜线(\)
return False
return True
def dfs(row): # 第row行皇后放于何处
global cnt # 将cnt设置为全局变量
if row == n + 1: # 递归终点
# 即产生了一组解
cnt += 1
return
for i in range(1, n + 1): # 第i列
if check(row, i): # 检查第row行能不能放在第i列上
a[row] = i # 将皇后放到第row行第i列上
dfs(row + 1) # 求下一行
# 递归结束,即已经求出一解,回收皇后以便新一轮放置(回溯)
a[row] = 0
dfs(1)
print(cnt)
2.全排列问题
题目描述
代码实现
n = int(input())
used = [0] * 10
ans = [0] * 10
def DFS(k):
if k == n:
print(' ', ' '.join(map(str, ans[:n])))
return
for i in range(1, n + 1):
if used[i] == 0:
used[i] = 1
ans[k] = i
DFS(k + 1)
used[i] = 0
DFS(0)
3.不同路径数
题目描述
代码实现
n, m, k = map(int, input().split())
lst = [list(input().split()) for i in range(n)] # 二维数组
res = set()
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
def DFS(x, y, cnt, ans):
if cnt == k + 1:
res.add(ans)
return
for r in range(4):
xx = x + dx[r]
yy = y + dy[r]
if 0 <= xx < n and 0 <= yy < m:
ans += lst[xx][yy]
DFS(xx, yy, cnt + 1, ans)
ans = ans[:-1] # 回溯
ans = ''
for i in range(n):
for j in range(m):
DFS(i, j, 0, ans) # 起点
print(len(res))
4. 跳跃
思路分析
本题是一个求解二维网格中从左上角到右下角的最大路径和的动态规划算法
1.首先,输入n和m表示网格的行和列数,然后读入一个n行m列的矩阵dp作为网格,
其中dp[i][j]表示从起点(0,0)到网格中第i行第j列的位置时的路径权值。
2.接着,定义了一个列表index_move,其中包含了所有可以往右和下走的位置的偏移量。
对于每一个位置(i, j),通过遍历index_move列表,计算出上一步能够到达的所有位置的权
值,并将这些权值存储在一个列表res中。
3.接下来,更新dp[i][j],将其赋值为当前位置(i, j)的权值加上res中最大的权值。
如果res为空,则不进行任何更新。最终,返回dp[-1][-1]即可得到最大路径和。
代码实现
n,m = map(int,input().split())
dp = [list(map(int,input().split())) for _ in range(n)]
index_move = [(0,1),(0,2),(0,3),(1,0),(2,0),(3,0),(1,2),(2,1),(1,1)] # 只可以往右和下走
# 为啥(2,2)不可以? ---> 貌似是出题人的问题,不用计较
for i in range(n):
for j in range(m):
res = [] # 存储下一步能走的位置的权值
for dx, dy in index_move:
# 向上/左
x = i - dx # 为啥是减? --> 逆向操作: 计算上一步的权值
y = j - dy
'''
这样做可以保证在遍历之前所有的可达位置的时候,
它们的值都是已知的,从而可以取最大值。
如果采用正向操作,在遍历到某个位置时,
它前面的位置的值可能还没有计算出来,
(后面的每一步都是以前面每一步的值为基础的)
这样就无法得到最优解。
'''
if 0 <= x < n and 0 <= y < m:
res.append(dp[x][y])
dp[i][j] += max(res) if res else 0 # 添加权值最大值(每次走的这一步都是最优解)
## for k in dp:
## print(k)
## print()
print(dp[-1][-1])