每日一道剑指offer-矩阵中的路径

题目:

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

 

例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:

输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
 

提示:

1 <= board.length <= 200
1 <= board[i].length <= 200
board 和 word 仅由大小写英文字母组成。

解析:

这道题是典型的DFS(深度优先搜索)问题,在一个图中去搜索符合条件的的路径,DFS是从某一个起始点开始沿着某一条路径开始走,直到不能走为止,然后再往回走。一条路走到底,不撞南墙不回头。这样很符合递归的思想,一直递归到底,然后遇到终止条件,回溯即可。本题也是使用递归的方式(推荐), 不过也给出了非递归的方式(用栈实现),来一步步的实现DFS,更加直观,不过代码就比较冗杂。

第一种方法:递归法,DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。

def exist(board, word: str) -> bool:
    # 找到路径起始,然后枚举,DFS。
    # 首先找出来第一个元素,之后从第一个元素起始,进行DFS。
    # 将已经访问过的节点变为“0”,保证以后再回到该节点时不会被访问。
    # 然后回退,把之前做出的选择撤销,还原。
    def dfs(i,j,k):
        if i<0 or i>len(board)-1 or j<0 or j>len(board[0])-1: #越界终止条件
            return False
        if board[i][j] != word[k]: #矩阵元素与目标字符不同或者当前矩阵元素已访问过(要是节点访问过时,与word中的元素也会不同,因为访问过就为"0",所以可以将这两个终止条件合并)
            return False
        if k == len(word)-1:
            return True
        board[i][j] = "0"
        #朝当前元素的 上、下、左、右 四个方向开启下层递归
        res = dfs(i,j+1,k+1) or dfs(i,j-1,k+1) or dfs(i+1,j,k+1) or dfs(i-1,j,k+1)
        board[i][j] = word[k] #回退:如果当前路径上正在访问的这个节点满足board[a][b] = word[k],但是这一节点相邻的三个方向都不满足要求,就需要把这个点回退。
        return res

    index = 0
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j] == word[0]: #首先找到路径的起始节点
                if dfs(i,j,index): #然后DFS即可
                    return True
    return False

 

第二种方法:非递归法,利用栈的先进后出原则,实现深度优先遍历,顺着一节点开始一直往下走,走不动了再回溯回来。

# 深度优先搜索遍历(DFS)---非递归方式(便于理解过程)--用栈。
def exist(board, word: str) -> bool:
    stack_1, stack_2 = [], [] #两个栈,一个栈保存所有路径,一个节点的上下左右;另外一个栈保存跟题目要求路径相同的节点,也就是已经访问的节点(来记录之前一步的位置)。
    index = 0
    if len(word) == 0:
        return False
    #确定首节点,加入栈1中(也就是确定要求路径首节点在矩阵中的位置,然后从这个位置开始,在相邻节点中进行判断那条路径可以走)
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j]==word[index]:
                stack_1.append((i, j))
    while stack_1:
        x, y = stack_1[-1][0], stack_1[-1][1] #获取栈顶元素指针x,y
        if x < 0 or x > len(board) - 1 or y < 0 or y > len(board[0]) - 1: #当栈顶元素指针越界时,直接弹出该元素指针即可
            stack_1.pop()
        elif board[x][y] == word[index] and board[x][y] != "0": #当矩阵中的当前节点和要求路径节点相同并且没有访问过的时候
            if index == len(word) - 1: #当指针移动到要求路径的末尾时,证明在矩阵中找到了要求路径。
                return True
            board[x][y] = "0" #当前节点等于要求路径节点时,设置为0,证明已经访问过。
            index += 1 #index继续前进,获得路径的下一个点
            stack_2.append((x, y)) #加入以访问的节点栈2中,用来记录之前一步的位置
            # 将当前节点的上下左右邻接点加入栈1中(也就是当前节点下一步的所有路径)
            stack_1.append((x, y + 1))
            stack_1.append((x, y - 1))
            stack_1.append((x + 1, y))
            stack_1.append((x - 1, y))
        else:
            # 回溯的时候如果回溯到上一步(之前记录的那一步的位置)则还原(当栈1中出栈时(回溯),遇到跟栈2中相同的元素(也就是回溯到了上一步,已经处理完的一步),
            # 然后进行还原(将index指针复位,设置为0的节点恢复,将两个栈的栈顶节点退出))(把之前做出的选择撤销)
            if stack_2 and stack_2[-1] == stack_1[-1]:
                index -= 1
                board[x][y] = word[index]
                stack_2.pop()
                stack_1.pop()
            else:
                stack_1.pop()
    return False #当栈1(矩阵元素)元素出栈完毕,证明在矩阵中没有找到该路径。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值