矩阵中的路径【剑指offer】-回溯和BFS

矩阵中的路径【剑指offer】- 回溯、BFS

  • 题目:
    在这里插入图片描述
  • 输入:path = “ABCCED” ,matrix = “ABCESFCSADEE”,rows = 3,cols = 4
  • 输出: True or False

1. 回溯法(递归):

  • 思路:
    • 首先矩阵中每一个字母都有可能是path的开头,因此遍历所有矩阵元素,只要有一次递归返回True,则返回True;
    • 递归思路:
      • 每一步递归做什么:给定当前matrix、当前位置r,c、当前path、和path中的第i个元素,判断从矩阵从(r,c)开始,path从第i个元素开始,两者是否匹配;
      • 终止条件:
        1. 如果i==len(path),返回True,说明指针i指到了len(path)位置,说明path前面元素都匹配完毕;
        2. 如果(r,c)越界了或者(r,c)位置已经被访问过了,返回False;
        3. 如果(r,c)位置元素和path[i]不匹配,返回False;
      • 返回什么:
        • 如果(r,c)和path[i]匹配成功,先更改matrix使得当前位置置为‘#’(已遍历)(该更改后的matrix会被直接传到下一轮递归),再判断下一位置是否匹配。下一位置有四种:上、下、左、右,使用or连接,只要有一个返回True就可以了

该方法和官方方法不同之处:不需要新建一个visted矩阵来记录遍历情况,直接在matrix上进行更改,并将改变好的matrix传入下一次递归。但是需要注意的地方在于第10行,因为矩阵的每个字母都有可能作为开头,因此遍历所有字母时,传入的matrix应该是同样干净的(没有被遍历过),因此使用了一个深拷贝(python3也可以使用copy方法)。

class Solution:
    def hasPath(self, matrix, rows, cols, path):
        # write code here
        if not matrix or not path:
            return False
        matrix = list(matrix) #转换成list方便记录走过的位置
        hasornot = False
        for r in range(rows):
            for c in range(cols):
                if self.haspath(matrix[:], rows, cols, r, c, path, 0):
                    return True
        return False
    def haspath(self, matrix, rows, cols, r, c, path, i):
        # 返回什么:返回matrix从(r,c)位置开始,path从i位置开始,两者是否匹配
        if i==len(path):
            return True
        if not 0<=r<rows or not 0<=c<cols or matrix[r*cols + c] == '#':# 越界或者已经访问过了
            return False
        if matrix[r*cols + c] != path[i]: # 不匹配
            return False
        else:                             # 匹配
            matrix[r*cols + c] = '#'      # 将当前位置置为visited
            return self.haspath(matrix, rows, cols, r, c+1, path, i+1) or \
                   self.haspath(matrix, rows, cols, r, c-1, path, i+1) or \
                   self.haspath(matrix, rows, cols, r+1, c, path, i+1) or \
                   self.haspath(matrix, rows, cols, r-1, c, path, i+1)

2. BFS广度优先遍历:

思考:

  • 递归的时候,需要不断向子递归函数传入当前已经遍历过的矩阵矩阵检测位置(r,c+1)/(r,c-1)/(r+1,c)(r-1,c),还有path的检测位置(i+1)。这三者的存储也可以通过BFS来实现,设想一颗四叉树,每一个节点存储(当前遍历过的矩阵,矩阵检测位置,path检测位置),然后只要path的检测位置i移动到了最后,就返回True,否则继续建立新的子节点(四个方向)

这里需要注意的地方:第10行处的matrix和path必须是深拷贝,原因同上。(这里之所以path也深拷贝是因为没有使用指针i来遍历path,而是直接遍历一个pop一个,因此path是动态的,所以在初始化时要深拷贝)

还有在存储四个子节点时,cur_matrix和cur_path同样需要深度拷贝,因为两者都是动态的,有人可能会问为什么回溯法中调用子递归函数时不需要深度拷贝?那是因为递归函数在参数传入的过程就已经深度拷贝了。

因此无论哪种方法,必然都要遍历所有可能的路径,空间消耗也是一样的。

class Solution:
    def hasPath(self, matrix, rows, cols, path):
        if not path:
            return False
        # 给定的matrix是个字符串,将该字符串转变成真正的matrix
        matrix = list(matrix)
        path = list(path)
        for i in range(rows):
            for j in range(cols):
               if self.BFS_search(matrix[:], i, j, rows, cols, path[:]):
                return True 
        return False
    def BFS_search(self, matrix, i, j, rows, cols, path):
        queue = []
        queue.append(((i,j), matrix, path))
        while queue:
            cur_pos, cur_matrix, cur_path = queue.pop(0)
            if not cur_path:
                return True
            r = cur_pos[0]
            c = cur_pos[1]
            if 0<=r<rows and 0<=c<cols and  cur_matrix[r*cols + c] != '#' and matrix[r*cols + c] == cur_path[0]:
                cur_matrix[r*cols + c] = '#'
                cur_path.pop(0)
                queue.append(((r+1,c), cur_matrix[:], cur_path[:]))
                queue.append(((r-1,c), cur_matrix[:], cur_path[:]))
                queue.append(((r,c+1), cur_matrix[:], cur_path[:]))
                queue.append(((r,c-1), cur_matrix[:], cur_path[:]))
        return False
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章