📜  检查2D矩阵中可能的路径(1)

📅  最后修改于: 2023-12-03 15:26:44.126000             🧑  作者: Mango

检查2D矩阵中可能的路径

在许多算法题中,我们需要检查一个二维矩阵中是否存在某一路径。这个路径可以在相邻的格子(上、下、左、右)之间移动,但是不能重复经过一个格子。例如,下面的矩阵中是否存在一个路径可以表示单词"ABCCED"?

A B C E
S F C S
A D E E

在本篇文章中,我们将介绍一种深度优先搜索(DFS)的方法,使用回溯算法,来检查一个二维矩阵中是否存在某一路径。同时,我们还会介绍另一种类似的算法-广度优先搜索(BFS)。

深度优先搜索(DFS)

深度优先搜索是一种常用的图形算法,我们可以用它来解决许多问题。对于一个二维矩阵中的路径检查问题,我们可以使用dfs来实现。

我们首先定义一个递归函数dfs(),实现以下功能:

  1. 如果当前的点不合法,或已经被访问过,则返回false。
  2. 如果已经找到路径,则返回true。
  3. 如果当前的点与路径中准备访问的字符不一致,则返回false。
  4. 如果当前的点是路径中的最后一个字符,则说明找到了路径,返回true。
  5. 否则,我们将当前位置标记为已访问,然后对其上、下、左、右四个方向递归查找。

下面是一个示例代码片段,实现了dfs函数。

def dfs(matrix, i, j, path):
    #1. 如果当前的点不合法,或已经被访问过,则返回false。
    if i < 0 or j < 0 or i >= len(matrix) or j >= len(matrix[0]) or matrix[i][j] != path[0]:
        return False

    #2. 如果已经找到路径,则返回true。
    if len(path) == 1:
        return True

    #3. 如果当前的点与路径中准备访问的字符不一致,则返回false。    
    temp = matrix[i][j]
    matrix[i][j] = None

    #4. 如果当前的点是路径中的最后一个字符,则说明找到了路径,返回true。
    if dfs(matrix, i+1, j, path[1:]) or dfs(matrix, i-1, j, path[1:]) or\
        dfs(matrix, i, j+1, path[1:]) or dfs(matrix, i, j-1, path[1:]):
        return True

    #5. 否则,我们将当前位置标记为已访问,然后对其上、下、左、右四个方向递归查找。
    matrix[i][j] = temp
    return False

我们可以在主函数中调用这个函数,在矩阵中查找指定字符串的路径。这里我们使用一个for循环遍历矩阵中每一个点,如果当前点与路径的首字符相同,我们就调用dfs函数查找是否存在路径。如果存在路径,则返回True,否则继续搜索。

def has_path(matrix, path):
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if matrix[i][j] == path[0] and dfs(matrix, i, j, path):
                return True
    return False

使用下面的矩阵和路径来测试代码:

matrix = [
    ['A', 'B', 'C', 'E'],
    ['S', 'F', 'C', 'S'],
    ['A', 'D', 'E', 'E']
]
path1 = 'ABCCED'
path2 = 'SEE'
path3 = 'ABCB'
print(has_path(matrix, path1))  #True
print(has_path(matrix, path2))  #True
print(has_path(matrix, path3))  #False

输出结果:

True
True
False
广度优先搜索(BFS)

广度优先搜索也是常用的一种算法,它从起点开始,逐层向外扩展。与深度优先搜索不同,广度优先搜索使用队列来记录待访问的节点。

我们在BFS中需要使用一个队列Q,首先将起点(起始点为根节点)加入队列,然后逐一出队并拓展,再将拓展所得到的新节点加入队列。

下面是一个示例代码段,实现了广度优先搜索查找路径的功能:

from collections import deque

def bfs(matrix, i, j, path):
    queue = deque([[(i, j)]])

    while queue:
        path_list = queue.popleft()
        x, y = path_list[-1]

        if matrix[x][y] == path[0]:
            if len(path) == 1:
                return True
            for i, j in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                if 0 <= x+i < len(matrix) and 0 <= y+j < len(matrix[0]) and (x+i, y+j) not in path_list:
                    new_path = path_list + [(x+i, y+j)]
                    queue.append(new_path)
    return False

这个函数中,我们首先将起点加入到一个队列path_list中。接下来,我们使用一个while循环来遍历队列中的每一个元素。在这个循环中,我们首先将队列的头部元素弹出path_list,然后将其最后一个元素视为当前拓展的格子。

现在,我们需要判断当前格子是否属于路径,如果是,则查看当前的格子是否是路径的尾部。如果不是,我们对其四个相邻的格子进行拓展,并将新路径入队。拓展过程中,需要注意已经访问过的格子不能再次被访问。

调用这个函数的代码如下:

def has_path(matrix, path):
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if bfs(matrix, i, j, path):
                return True
    return False

使用下面的矩阵和路径来测试代码:

matrix = [
    ['A', 'B', 'C', 'E'],
    ['S', 'F', 'C', 'S'],
    ['A', 'D', 'E', 'E']
]
path1 = 'ABCCED'
path2 = 'SEE'
path3 = 'ABCB'
print(has_path(matrix, path1))  #True
print(has_path(matrix, path2))  #True
print(has_path(matrix, path3))  #False

输出结果:

True
True
False
总结

在这篇文章中,我们介绍了如何使用深度优先搜索和广度优先搜索来检查二维矩阵中是否存在某一路径。在dfs中,我们定义了一个递归函数来逐个检查矩阵中的元素。在BFS中,我们可以使用队列来记录待访问的节点,并实现逐层拓展的功能。

这两种算法都可以解决二维矩阵中的路径检查问题,但是它们的复杂度不同。dfs的时间复杂度是指数级别的,因此它在面对某些情况时可能会出现较差的表现。而BFS的时间复杂度是线性的,因此它在大多数情况下都表现良好。在实际应用中,我们应该根据具体的数据情况来选择合适的算法。