[剑指-Offer] 12. 矩阵中的路径(回溯、DFS、递归、代码优化)

1. 题目来源

链接:矩阵中的路径
来源:LeetCode——《剑指-Offer》专项

2. 题目说明

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的 3×4 的矩阵中包含一条字符串 “bfce” 的路径(路径中的字母用加粗标出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩阵中不包含字符串 “abfb” 的路径,因为字符串的第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

示例 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

3. 题目解析

方法一:DFS、回溯、bool矩阵、经典仿照原书

典型 DFS 应用,也就是回溯法,主要以下几点思路:

  • 创建一个和原数组等大小的 boolvisited 数组用来记录当前位置是否已经被访问过,在这里一维 vector 即可,根据坐标关系能够映射为二维坐标中
  • 如果二维数组 board 的当前字符和目标字符串 word 对应的字符相等,则对其上下左右四个邻字符分别调用 DFS 的递归函数,只要有一个返回 true,那么就表示可以找到对应的字符串,返回 true 即可
  • 当遍历完 board 还没找到即返回 false

参见代码如下:

// 执行用时 :12 ms, 在所有 C++ 提交中击败了18.23%的用户
// 内存消耗 :10.6 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int movingCount(int m, int n, int k) {
        int tmp = m * n;
        vector<bool> visited(tmp, false);
        int count = movingCountCore(m, n, k, 0, 0, visited);
        return count;
    }

    int movingCountCore(int m, int n, int k, int row, int col, vector<bool>& visited) {
        int count = 0;
        if (check(m, n, k, row, col, visited)) {
            visited[row * n + col] = true;
            count = 1 + movingCountCore(m, n, k, row - 1, col, visited) 
                      + movingCountCore(m, n, k, row, col - 1, visited) 
                      + movingCountCore(m, n, k, row + 1, col, visited)
                      + movingCountCore(m, n, k, row, col + 1, visited);
        }
        return count;
    }

    bool check(int m, int n, int k, int row, int col, vector<bool>& visited) {
        if (row >= 0 && row < m && col >= 0 && col < n 
            && getDigitSum(row) + getDigitSum(col) <= k 
            && !visited[row * n + col])
            return true;
        return false;
    }

    int getDigitSum(int number) {
        int sum = 0;
        while (number > 0) {
            sum += number % 10;
            number /= 10;
        }
        return sum;
    }
};

方法二:DFS、回溯、修改原数组

还有一种选择,即可以不用 visited 数组,直接对 board 数组进行修改,将其遍历过的位置改为特定符号,记得递归调用完后需要恢复之前的状态,也是一个常见的优化技巧

参见代码如下:

// 执行用时 :12 ms, 在所有 C++ 提交中击败了18.23%的用户
// 内存消耗 :10.6 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        if (board.empty() || board[0].empty()) 
            return false;
        int m = board.size(), n = board[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (search(board, word, 0, i, j)) 
                    return true;
            }
        }
        return false;
    }
    bool search(vector<vector<char>>& board, string word, int idx, int i, int j) {
        if (idx == word.size()) 
            return true;
        int m = board.size(), n = board[0].size();
        if (i < 0 || j < 0 || i >= m || j >= n || board[i][j] != word[idx]) 
            return false;    
        char c = board[i][j];
        board[i][j] = '#';
        bool res = search(board, word, idx + 1, i - 1, j) 
                 || search(board, word, idx + 1, i + 1, j)
                 || search(board, word, idx + 1, i, j - 1)
                 || search(board, word, idx + 1, i, j + 1);
        board[i][j] = c;
        return res;
    }
};
发布了307 篇原创文章 · 获赞 125 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/104480988
今日推荐