《剑指Offer》 -- 矩阵中的短路径(回溯法)(Python实现)

题目描述:

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 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)停止递归条件

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值