bfs题目
前言
构建图
graph={
"A": ["B","C"],
"B": ["A","C","D"],
"C": ["A","B","D","E"],
"D": ["B","C","E","F"],
"E": ["C","D"],
"F": ["D"]
}
注意:
- pop(0)弹出第一个数
- pop()弹出最后一个数
DFS 与 BFS 在寻找路径最不同的一点:
- DFS 是一条路走到黑,它适合用于 「是否存在一条路径」
- 而 BFS 是影分身,每次影分身都会在原基础上走一步,适合用于 「是否存在一条最短路径」
bfs(队列,先进先出)
def BFS(graph,s):
queue=[]#队列
queue.append(s)
seen=set()#已访问过的集合
seen.add(s)
while(len(queue)>0):
vertex=queue.pop(0)#弹出第一个元素
nodes=graph[vertex]
for w in nodes:
if w not in seen:
queue.append(w)
seen.add(w)
print(vertex)
BFS(graph,"A")#A B C D E F
dfs(栈,先进后出)
def DFS(graph,s):
stack=[]#栈
stack.append(s)
seen=set()#已访问过的集合
seen.add(s)
while(len(stack)>0):
vertex=stack.pop()#弹出最后一个元素
nodes=graph[vertex]
for w in nodes:
if w not in seen:
stack.append(w)
seen.add(w)
print(vertex)
DFS(graph,"A")#A C E D F B
bfs&dfs题目
783.二叉搜索树节点最小距离(中序遍历)
- 先中序遍历,把结果防在数组中;
- 然后对数组中的相邻元素求差,得到所有差值的最小值
xrange() 函数用法与 range 完全相同,所不同的是生成的不是一个数组,而是一个生成器。
xrange(3, 5)#xrange(3, 5)
list(xrange(3,5))#[3, 4]
range(3,5)# 使用 range[3, 4]
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def minDiffInBST(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.vals=[]
self.dfs(root)
return min([self.vals[i+1]-self.vals[i] for i in xrange(len(self.vals)-1)])
def dfs(self,root):
if not root:
return
self.dfs(root.left)
self.vals.append(root.val)
self.dfs(root.right)
2059.转换数字的最小运算数(BFS)
算法思路:
在广度优先搜索的过程中,step为当前值对应的转化次数。
(从题干得出!!!)
注意到如果 x不在可以操作的范围(本题为 [0, 1000]闭区间内的整数)内,除非x=goal 恰好成立,否则由于我们无法进行任何操作,该数一定无法转化为目标值。
故我们无需将可操作范围以外的数值加入队列。且由于初始值一定在可操作范围内,因此我们可以保证队列中的值一定在可操作范围内。
除此以外,为了避免重复遍历,我们需要用数组vis 来维护可操作范围内整数是否已被加入过队列。
当我们遍历到 x 时,我们枚举数组中的元素和加、减与按位异或三种操作,计算生成的值y,此时有以下几种情况:
- y恰好等于目标值goal,此时我们应当返回step+1,即初始值转化为目标值的最小次数作为答案;
- y不在可操作范围,此时无需任何操作;
- y在可操作范围内,且y已被加入过队列,此时无需任何操作;
- y在可操作范围,且y未被加入过队列,此时我们需要更新y的访问情况,并将y加入队列,更新step+1;
- 最终,不存在答案返回-1;
参考BFS模板,加上步数统计
class Solution(object):
def minimumOperations(self, nums, start, goal):
"""
:type nums: List[int]
:type start: int
:type goal: int
:rtype: int
"""
queue=[]
queue.append(start)
seen=set()
seen.add(start)
step=0#记录步数
while(len(queue)>0):
step+=1#step是在这加1的!
for _ in range(len(queue)):#用来累积步数的
x=queue.pop(0)#弹出第一个元素
for num in nums:
for y in (x+num,x-num,x^num):
if y==goal:
return step
if 0<=y<=1000 and y not in seen:
queue.append(y)
seen.add(y)
return -1
1219.黄金矿工
算法思路:回溯算法
首先在mXn个网格内枚举起点。只要格子内的数大于0,它就可以作为起点进行开采。记枚举的起点为(i,j),我们就可以从(i,j)开始进行递归+回溯,枚举所有可行的开采路径。我们用递归函数dfs(x,y,gold)进行枚举,其中(x,y)表示所在的位置,gold表示开采位置(x,y)之前,已拥有的黄金数量。根据题目的要求,我们需要进行如下步骤:
- 我们需要将gold更新为gold+grid[x][y],表示对位置(x,y)进行开采。由于我们的目标是最大化收益,因此我们还要维护一个最大的收益值ans,并在这一步使用gold更新ans;
- 我们需要枚举矿工下一步的方向。由于矿工每次可以从当前位置向上下左右四个方向走,因此我们需要依次枚举每一个方向。如果往某一方向不会走出网格,并且走到的位置的值不为0,我们就可以进行递归搜索;
- 在搜索完所有方向后,我们进行回溯。
需要注意的是,题目规定了“每个单元格只能被开采一次”,因此当前我们到达位置(x,y)时,我们可以将grid[x][y]暂时置为0;在进行回溯之前,再将grid[x][y]的值恢复。
class Solution(object):
def getMaximumGold(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m=len(grid)
n=len(grid[0])
global ans
ans=0
def dfs(x,y,gold):
gold=gold+grid[x][y]
global ans
ans=max(ans,gold)
res=grid[x][y]
grid[x][y]=0#遍历过,标记
for nx,ny in ((x-1,y),(x+1,y),(x,y-1),(x,y+1)):
if 0<=nx<m and 0<=ny<n and grid[nx][ny]>0:#符合条件遍历
dfs(nx,ny,gold)
grid[x][y]=res#遍历完恢复原来值
for i in range(m):
for j in range(n):
if grid[i][j]!=0:#依次遍历
dfs(i,j,0)
return ans
LCP 07.传递信息(dfs)
算法思路:暴力的深度搜索
class Solution(object):
def numWays(self, n, relation, k):
"""
:type n: int
:type relation: List[List[int]]
:type k: int
:rtype: int
"""
adjvex=collections.defaultdict(set)#python里也可用
for x,y in relation:
adjvex[x].add(y)#x位置,y为能传到的地方,可能不止一个
def dfs(x,step):
if step==k:#到达规定的轮数
if x==n-1:#并且到最后一个点
self.res+=1#结果加1
return
for y in adjvex[x]:
dfs(y,step+1)
self.res=0
dfs(0,0)#初始值:位置0,步数0
return self.res
模拟、字符串
6.Z字形变换
class Solution(object):
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if numRows < 2: return s
res = ["" for _ in range(numRows)]
i, flag = 0, -1
for c in s:
res[i] += c
if i == 0 or i == numRows - 1: flag = -flag#flag用得妙
i += flag
return "".join(res)
往届真题
蛇形填数
解题思路:找规律,模拟
找到一个很关键的规律
ij坐标相加是奇数,←↓走势
ij坐标相加是偶数,→↑走势
i,j,num=0,0,0#初始化坐标和计数器
while True:#无限循环开始
num+=1#元素递增
if i==19 and j==19:#从0遍历到19, 相当于20行20列
break
if (i+j)&1:#奇数↙
i+=1
if j>0:
j-=1
else:#偶数↗
j+=1
if i>0:
i-=1
print(num)#761