题目描述:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bfce"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
分析:
回溯法思路的简单描述是:把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。
我个人理解回溯法最重要的一步就是找到一个正确的根部,也就是第一步,如果没有找到正确的根部,后面的节点即使是正确的,最终的结果也是徒劳的。
第一步,在脑海中将原来的矩阵转成有图所示的树状结构(循环查找)
第二步,沿着根部一直向下搜索,每一个节点只是搜索相邻的四个元素(递归搜索)
代码说明一切,注意:以下代码中path就是需要寻找的指定路径
def hasPath(matrix, rows, cols, path):
# 输入错误参数直接返回
if matrix is None or path is None or rows < 1 or cols < 1 or rows * \
cols != len(matrix):
return False
# 将矩阵转成二维数组,方便遍历
array = list(matrix)
array = [array[i * cols: (i + 1) * cols] for i in range(rows)]
# 循环遍历寻找正确的根,也就是寻找与 path[0] 对应的根节点
# 只有找到正确的根部才能往下继续找到正确的节点
for row in range(rows):
for col in range(cols):
# visited 数组用来记录访问过的节点,只有当某个节点为目标节点时,visited数组中相
# 应的位置才会标为True
# 每次找到新的根部的时,都要将 visited 数组重新初始化
# 如果没有重新初始化的话,上一次寻找出来的根部,沿着根部往下访问过的节点会被记录并
# 且对应的位置为True,
# 当重新找到新的根部以后,曾经访问过得节点不会再次被访问
# 因此需要被重新初始化
visited = [[False for i in range(cols)] for i in range(rows)]
if hasPathCore(array, row, col, path, visited):
return True
del visited
# 遍历所有元素且没有发现与path[0]相对应的根节点
# 直接返回False
return False
def hasPathCore(array, row, col, path, visited):
# 递归必要条件1:设定边界条件防止越界溢出
if row < 0 or row >= len(array) or col < 0 or col >= len(
array[0]) or visited[row][col]:
return False
# 递归返回必要条件2:设定递归返回所满足的条件,防止无限递归
if path[0] == array[row][col]:
# 当前节点元素是正确节点
visited[row][col] = True
# 如果当前要找的路径长度为1时,即已经递归到了路径的末尾
# 返回
if len(path) == 1:
return True
# 如果不是所求路径的末尾,则沿着当前节点继续往下搜索
# 搜索规则:假设当前节点为(row, col),则搜索当前节点的四个相邻节点
# (row, col-1),(row, col+1),(row-1, col),(row+1, col)
#
# path[1:]代表递归时的搜索目标是当前路径节点的下一个节点
if hasPathCore(array, row, col - 1, path[1:], visited) or \
hasPathCore(array, row, col + 1, path[1:], visited) or \
hasPathCore(array, row + 1, col, path[1:], visited) or \
hasPathCore(array, row - 1, col, path[1:], visited):
return True
return False
else:
return False
print(hasPath("ABCEHJIG\
SFCSLOPQ\
ADEEMNOE\
ADIDEJFM\
VCEIFGGS",5,8,"SLHECCEIDEJFGGFIE"))
总结:
1、回溯法的思想就是将把问题的解空间转化成了图或者树的结构表示,回溯法非常适合由多个步骤组成的问题,并且每个步骤都有多个选项
2、回溯法适应场景:当我们在某一步选择了其中一个选项时,就进入了下一步,然后面临新的选项。但是一旦发生错误,无法继续往下寻找时,返回上一步,重新搜索正确的上一步。
3、面对上述问题,将问题形象描述成树状结构(类似决策树)
4、两步走,1)寻找到正确的根部 ;2)沿着根部使用递归方法往下寻找正确的节点
5、递归注意要素:1)边界问题;2)停止递归条件