算法# 学习目标:优先搜索算法(四 )
学习内容:
优先搜索算法:包括深度优先搜索和广度优先搜索;深度优先搜索(DFS):在搜索到一个新节点时,立即对该新节点进行遍历;因此遍历需要用先入后出的栈来实现,也可以通过与栈等价的递归来实现;广度优先搜索(BFS):一层层进行遍历,需要用先入先出的队列进行遍历。
学习产出:
广度优先搜索
广度优先搜索
不同于深度优先搜索,他是一层层进行遍历,需要用先入先出的队列进行遍历。由于是按层次进行遍历,广度优先搜索按照“广”的方向进行遍历,常用来处理最短路径问题
LeetCode 934 最短的桥
在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)
现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。
返回必须翻转的 0 的最小数目。(可以保证答案至少是 1。)
示例 1:
输入:[[0,1],[1,0]]
输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-bridge
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
实际求解两个岛最短路径问题,可使用任意搜索方法找到其中一个岛,然后广度优先搜索,求于另一个岛的最短距离
代码(python)
class Solution:
def shortestBridge(self, A: List[List[int]]) -> int:
direction = [-1,0,1,0,-1] #方位列表
m,n = len(A),len(A[0]) #行、列
points = [] #初始化队
flipped = False #标记
def dfs(points, grid,i,j): #深度优先搜索搜索第一个岛
if i<0 or j<0 or i==m or j==n or grid[i][j] ==2:
return
if grid[i][j] == 0:
points.append([i,j]) #海面位置写入
return
grid[i][j] = 2
dfs(points, grid, i - 1, j)
dfs(points, grid, i + 1, j)
dfs(points, grid, i, j - 1)
dfs(points, grid, i, j + 1)
#dfs寻找第一个岛屿
for i in range(m):
if flipped : break
for j in range(n):
if A[i][j] == 1:
dfs(points,A,i,j)
flipped = True
break
#bfs寻找第二岛屿
level = 0
while points:
level += 1 #造陆次数统计
n_points = len(points)
while n_points:
[r,c]=points.pop(0) #出队
for k in range(4):
x = r+direction[k]
y = c+direction[k+1]
if x>=0 and y>=0 and x<m and y<n:
if A[x][y] == 2:
continue
if A[x][y] == 1:
print(points)
return level
points.append([x,y])
A[x][y] = 2
n_points -= 1
LeetCode 126 单词接龙 II
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
每次转换只能改变一个字母。
转换后得到的单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-ladder-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
把起始字符串,终止字符串,以及单词表里所有字符串想象成节点,如果两个字符串只有一个字符不同,那它们相连。因为题目要求输出修改次数最少的所有修改方式,所以可以使用广度优先搜索,求起始点到终止点的最短距离。同时采用两端搜索即从起始点和终止点分别进行搜索,每次只延展当前层节点数最少的那一端,这样可以减少搜索的总节点数。在结束后,还需要回溯重建所有可能的路径。
代码(python)
class Solution:
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
path = [] #回溯路径
ans = [] #结果
dict = wordList
if not dict.count(endWord): #不存在结束字符直接结束
return ans
#删除字典里的开始和结束字符
if beginWord in dict:
dict.remove(beginWord)
if endWord in dict:
dict.remove(endWord)
q1,q2=[beginWord], [endWord]
revers,found = False,False #标记
next = collections.defaultdict(list) #初始化字典用于记录遍历流程
def backtracking(src,dst,next,path,ans): #回溯算法
if src == dst:
ans.append(path[:])
return
for s in next[src]:
path.append(s) #修改当前节点
backtracking(s,dst,next,path,ans) #递归子节点
path.pop() #回改当前节点
#修改起始字符寻找字典有的词
while q1!=[]:
q = []
for i in range(len(q1)):
w = q1[i]
s = list(q1[i])
for i in range(len(s)): #遍历字符每一位
ch = s[i]
for j in range(26):
s[i] = chr(j+ ord('a'))
s1 = ''.join(s)
#遍历搜索满足条件的单词,并保存在next字典中
if q2.count(s1):
if revers:
next[s1].append(w)
else: next[w].append(s1)
found = True
if dict.count(s1):
if revers:
next[s1].append(w)
else: next[w].append(s1)
q.append(s1)
s[i] = ch
if found:
break
for i in range(len(q)):
if q[i] in dict: #删除已经遍历的单词
dict.remove(q[i])
if len(q) <= len(q2):
q1 = q
else:
revers = not revers
q1 = q2
q2 = q
if found:
path=[beginWord]
backtracking(beginWord,endWord,next,path,ans)
return ans
注:结果正确,但是与力扣的结果,在排序位置有所不同,有待改进结果,插个眼记一下