原题链接:点击
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.
这其实是深度优先搜索(DFS)+回溯(BT)的一种常见题型。很明显,我们首先要找到初始搜索位置,然后根据这个搜索位置上、下、左、右依次查找是否匹配下一个字符,在搜索的过程中还要注意边界条件的约束,一旦找到匹配成功的字符,记录它确保下次不被访问,并继而据该字符进行同样的搜索,直到搜索到最后一个字符,返回结果;这是顺利的情形,但实际上我们会有匹配失败的情形出现,即据该字符的四周无法找到一个合适的字符,那么就应该回溯,并消除该字符被访问过的记录。
其实说到底就是标准的回溯思想,消化一下不难理解代码:
# 上下左右
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
def exist(board, word):
m = len(board)
if m == 0:
return False
n = len(board[0])
# 通过找到首字母进行切入搜索
firstLetter = word[0]
for i in range(m):
for j in range(n):
if firstLetter == board[i][j]:
# 从首字符匹配的那个开始搜索
# 如果匹配成功 就可以直接返回true
# visited数组记录访问过的元素
visited = set()
visited.add((i, j))
if dfs(i, j, board, index, word, m, n, visited):
return True
return False
# 递归函数
def dfs(x, y, board, index, word, m, n, visited):
# DFS搜索
# index表示当前匹配到的word索引
if index == len(word)-1:
return word[index] == board[x][y]
for d in directions:
curX, curY = x + d[0], y + d[1]
# 约束条件 没被访问 且 下一个字符匹配成功
# 我们就返回 true
if 0 <= curX <= m-1 and <= curY <= n-1 and
(curX, curY) not in visited and dfs(curX, curY, board, index+1, word, m, n, visited):
return True
# 回溯
visited.remove((x, y))
return False
代码理解起来不难,就是函数传参有点多…并且还另外开辟了集合用于保存访问过的变量,其中搜索过程也需要一定的时间复杂度。对此,我们还可以写出这种易懂的形式:
def exist(board, word):
m = len(board)
if m == 0:
return False
n = len(board[0])
# 通过一个布尔量来记录是否成功找到字符串
self.flag = False
for i in range(m):
for j in range(n):
if word[0]== board[i][j]:
dfs(i, j, 0, m, n, board, word)
return self.flag
def dfs(x, y, index, m, n, board, word):
# DFS搜索
# 并利用 board 来记录状态
# index表示当前匹配到的word索引
# 函数递归返回的情形
# 1.边界条件
# 2.查找索引大于查找字符串长度
# 3.匹配失败
# 4.已经找到
if x < 0 or y < 0 or x >= m or y >= n or index >= len(word) or board[x][y] != word[index] or self.flag:
return
if index == len(word)-1:
self.flag = True
# 递归+回溯模板
oldValue = board[x][y]
board[x][y] = ' '
dfs(x+1, y, index+1, m, n, board, word)
dfs(x, y+1, index+1, m, n, board, word)
dfs(x-1, y, index+1, m, n, board, word)
dfs(x, y-1, index+1, m, n, board, word)
board[x][y] = oldValue
这里有意思的还有一点,就是递归函数中参数的传递形式问题。可以尝试分别使用值传递和引用传递
的方式运行程序,从结果上来看似乎引用传递
的效率会更好,具体的原因不是很清楚,想必和递归运行时栈的开辟与参数变量的保存有关系,似乎存在参数在函数内没有发生改变时,使用引用传递
会提升效率…