【剑指offer-C++】JZ12:矩阵中的路径

【剑指offer】JZ12:矩阵中的路径

题目描述

描述:请设计一个函数,用来判断在一个n乘m的矩阵中是否存在一条包含某长度为len的字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
abce
sfcs
adee
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

数据范围: 0≤n,m≤20 ,1≤len≤25 。

输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"
返回值:true
输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcb"
返回值:false

解题思路

矩阵中的路径:最直观的想法是,首先使用stxy变量存放矩阵matrix中出现的所有word起始位置元素的下标,然后以这些下标为起始开始dfs遍历,并使用tfxy变量存放矩阵matrix中的元素是否被访问,判断是否存在一条字符串路径,如果存在则直接返回true,如果所有均遍历完后仍不存在,则返回false。其中dfs遍历是这样的,首先判断字符串word是否已经访问结束,如果是则返回true,然后判断矩阵下标是否合法,如果不合法则返回false,接着判断矩阵元素是否等于字符串元素,如果是则记录元素已访问,并且返回上下左右四个方向的遍历。

(以下这个代码是最原始的思路写的,有一个案例无法通过)

bool dfs(vector<vector<char>>& matrix, vector<vector<bool>>& tfxy, int i, int j,
             string& word, int index) {
        if (index >= word.size()) //先要处理这个判断条件 否则[['A','A'],['A','A']] "AAAA" 就会出现错误
            return true;
        if (i < 0 || i >= matrix.size() || j < 0 || j >= matrix[0].size() ||
                tfxy[i][j] == true) //处理非法下标
            return false;
        if (word[index] == matrix[i][j]) {
            tfxy[i][j] = true;
            return dfs(matrix, tfxy, i + 1, j, word, index + 1) ||
                   dfs(matrix, tfxy, i - 1, j, word, index + 1) ||
                   dfs(matrix, tfxy, i, j + 1, word, index + 1) ||
                   dfs(matrix, tfxy, i, j - 1, word, index + 1);
        } else
            return false; //不相等返回false
        return false;
    }
    bool hasPath(vector<vector<char> >& matrix, string word) {
        int n = matrix.size(), m = matrix[0].size(), len = word.size();
        if (len > n * m) return false;
        char start = word[0];
        vector<vector<int>> stxy;
        for (int i = 0; i < n; i++) //寻找起始节点
            for (int j = 0; j < m; j++)
                if (matrix[i][j] == start)
                    stxy.push_back({i, j});
        for (auto stxys : stxy) {
            vector<vector<bool>> tfxy(n, vector<bool>(m, false)); //标记是否访问
            bool flag = dfs(matrix, tfxy, stxys[0], stxys[1], word, 0);
            if (flag == true) return true;
        }
        return false;
    }

问题:但是这里存在几个问题:第一个是字符串word是否已经访问结束和矩阵下标是否合法的顺序不能反,否则[[‘A’,‘A’],[‘A’,‘A’]] "AAAA"就会出现错误;第二个是[[A,B,C],[B,E,D],[F,G,G]],"ABCDEBF"或者[[A,B,F],[B,E,G],[C,D,G]],"ABCDEBF"两个案例无法通过,这就让我想起来,如果某一个元素其有多个方向是相同的下一个元素,但是按照我上述的方法dfs遍历后,如果先遍历的那一个方向是一个死胡同,那么程序是直接返回false的,但是事实上,我还可以回到原来的元素进行下一个方向的遍历,这明显是回溯法,于是最后通过的程序如下。

(以下这个代码是按照改进的思路优化写的,案例全部通过)

bool dfs(vector<vector<char>>& matrix, vector<vector<bool>>& tfxy, int i, int j,string& word, int index) 
{
    if (index >= word.size()) 
    //先要处理这个判断条件 否则[['A','A'],['A','A']] "AAAA" 就会出现错误
        return true;
    if (i < 0 || i >= matrix.size() || j < 0 || j >= matrix[0].size()||
    matrix[i][j] !=word[index]||tfxy[i][j] == true) //处理非法下标
        return false;
    tfxy[i][j] = true;
    bool res=dfs(matrix, tfxy, i, j + 1, word, index + 1) ||
    dfs(matrix, tfxy, i + 1, j, word, index + 1) ||
    dfs(matrix, tfxy, i - 1, j, word, index + 1)||
    dfs(matrix, tfxy, i, j - 1, word, index + 1);
    tfxy[i][j]=false; 
    //利用回溯法 否则[[A,B,C],[B,E,D],[F,G,G]],"ABCDEBF"会出错
    //[[A,B,F],[B,E,G],[C,D,G]],"ABCDEBF"
    return res; 
}
bool hasPath(vector<vector<char> >& matrix, string word) {
    int n = matrix.size(), m = matrix[0].size(), len = word.size();
    if (len > n * m) return false;
    char start = word[0];
    vector<vector<int>> stxy;
    for (int i = 0; i < n; i++) //寻找起始节点
       for (int j = 0; j < m; j++)
          if (matrix[i][j] == start)
              stxy.push_back({i, j});
    for (auto stxys : stxy) 
    {
        vector<vector<bool>> tfxy(n, vector<bool>(m, false)); //标记是否访问
        bool flag = dfs(matrix, tfxy, stxys[0], stxys[1], word, 0);
        if (flag == true) return true;
    }
    return false;
}

总结:返回true的写在一起,返回false的写在一起,注意先后顺序。任何事情都不是一蹴而就的,先有思路,再去实践,接着分析,不断优化。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值