矩陣中的路徑【劍指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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章