【剑指Offer记录】12_矩阵中的路径

Part1. 我的思路和代码

核心思想是深度优先遍历DFS,从一个位置开始不断深入搜索,遇到不符合条件的位置则回溯。我的思路分为起点选择、搜索策略、判断条件、变量设置四个方面。

起点选择: 以word的首字符为起点。用双循环判断矩阵中的每个位置是否和首字母相等,若是则将其作为起点进行DFS。

搜索策略: 目前的路径不为空(为空表示遍历完了当前起点的所有的可能,需要从其他起点再次DFS)时,先判断是否已经搜索完毕,若是则返回true,否则判断当前路径长度是否与word长度相同,若是则回溯(进行到这一步说明之前“判断是否搜索完毕”时判断出没有搜索完,长度相同但是内容不同),否则判断当前位置的元素是否符合“判断条件”(见下文),若是,则到下一个位置,否则回溯。

判断条件: 当前位置的行号、列号没有超过矩阵的边界,并且当前位置没有出现在目前的搜索路径中,见isAccessable()函数。

变量设置: 分为搜索路径信息、辅助标志两部分。搜索路径信息包括当前路径构成的字符串、行索引序列、列索引序列,分别对应变量path_val、path_rowId、path_colId(如果定义成结构体代码应该可读性会更好);辅助标志为当前搜索路径上每个位置向左/上/右/下搜索的情况,对应二维数组flag,其形状和matrix相同,flag[i][j]的值可以是0/1/2/3/4,初始为0表示没有搜索到,为1、2、3、4分别表示从(i, j)的位置已经向左/上/右/下搜索过,也就是当flag[i][j]分别为0、1、2、3时,下一个搜索位置分别为向左、向上、向下、向右,如果为4,则表示从(i, j)位置已经向四个方向都搜索过,需要回溯,回溯时flag[i][j]需要重新设置为0

class Solution {
public:

    void clearSearchRecord(vector<vector<char> >& flag, string& path_val, vector<int>& path_rowId, vector<int>& path_colId) { // 清空之前的搜索记录
        for(int ii=0; ii<flag.size(); ii++){
            for(int jj=0; jj<flag[0].size(); jj++){
                flag[ii][jj] = 0;
            }
        }
        path_val.clear();
        path_rowId.clear();
        path_colId.clear();
    }

    bool isAccessable(int row, int col, vector<vector<char> >& flag, vector<int>& path_rowId, vector<int>& path_colId) { // 判断 (row, col) 位置是否可以搜索,即不超过矩阵边界,且不在搜索过的路径上
        if(row<0 || row>=flag.size() || col<0 || col>=flag[0].size()){
            return false;
        }
        for(int i=0; i<path_rowId.size(); i++){
            if(row==path_rowId[i] && col==path_colId[i]){
                return false;
            }
        }
        return true;
    }

    bool hasPath(vector<vector<char> >& matrix, string word) {
        int row_count;
        int col_count;
        vector<vector<char> >flag; // 0表示未搜索到,1、2、3、4分别表示从当前位置向左、上、右、下搜索过(char类型,但存整数值)
        string path_val;
        vector<int> path_rowId;
        vector<int> path_colId;
        int cur_row;
        int cur_col;

        row_count = matrix.size(); // 初始化
        col_count = matrix[0].size();
        flag = matrix;
        for(int i=0; i<row_count; i++){ // 开始搜索
            for(int j=0; j<col_count; j++){
                if(matrix[i][j] == word[0]){ // 找到了一个起点,重新开始搜索
                    cur_row = i;
                    cur_col = j;
                    clearSearchRecord(flag, path_val, path_rowId, path_colId);
                    path_val.push_back(matrix[cur_row][cur_col]); // 深度优先搜索
                    path_rowId.push_back(cur_row);
                    path_colId.push_back(cur_col);
                    while(!path_val.empty()){
                        if(path_val == word){ // 找到了一条路径
                            return true;
                        }
                        else  if(path_val.size() == word.size()){ // 找到一条长度与word相同的路径,但内容不同,回退一步
                            flag[path_rowId.back()][path_colId.back()] = 0;
                            path_val.pop_back();
                            path_rowId.pop_back();
                            path_colId.pop_back();
                            cur_row = path_rowId.back();
                            cur_col = path_colId.back();
                        }
                        else{ // 前进一步,或者回退一步
                            if(flag[cur_row][cur_col]<4 && path_val.back()==word[path_val.size()-1]){ // 前进一步
                                switch(flag[cur_row][cur_col]){
                                    case 0: //左
                                        if(isAccessable(cur_row, cur_col-1, flag, path_rowId, path_colId)){
                                            path_val.push_back(matrix[cur_row][cur_col-1]);
                                            path_rowId.push_back(cur_row);
                                            path_colId.push_back(cur_col-1);
                                            ++flag[cur_row][cur_col];
                                            cur_col = cur_col-1;
                                        }
                                        else{
                                            ++flag[cur_row][cur_col];
                                        }
                                        break;
                                    case 1: //上
                                        if(isAccessable(cur_row-1, cur_col, flag, path_rowId, path_colId)){
                                            path_val.push_back(matrix[cur_row-1][cur_col]);
                                            path_rowId.push_back(cur_row-1);
                                            path_colId.push_back(cur_col);
                                            ++flag[cur_row][cur_col];
                                            cur_row = cur_row-1;
                                        }
                                        else{
                                            ++flag[cur_row][cur_col];
                                        }
                                        break;
                                    case 2: //右
                                        if(isAccessable(cur_row, cur_col+1, flag, path_rowId, path_colId)){
                                            path_val.push_back(matrix[cur_row][cur_col+1]);
                                            path_rowId.push_back(cur_row);
                                            path_colId.push_back(cur_col+1);
                                            ++flag[cur_row][cur_col];
                                            cur_col = cur_col+1;
                                        }
                                        else{
                                            ++flag[cur_row][cur_col];
                                        }
                                        break;
                                    case 3: //下
                                        if(isAccessable(cur_row+1, cur_col, flag, path_rowId, path_colId)){
                                            path_val.push_back(matrix[cur_row+1][cur_col]);
                                            path_rowId.push_back(cur_row+1);
                                            path_colId.push_back(cur_col);
                                            ++flag[cur_row][cur_col];
                                            cur_row = cur_row+1;
                                        }
                                        else{
                                            ++flag[cur_row][cur_col];
                                        }
                                        break;
                                    default: break;
                                }
                            }
                            else{ // 回退一步
                                flag[path_rowId.back()][path_colId.back()] = 0;
                                path_val.pop_back();
                                path_rowId.pop_back();
                                path_colId.pop_back();
                                cur_row = path_rowId.back();
                                cur_col = path_colId.back();
                            }
                        }
                    }
                }
            }
        }

        return false;
    }
};

特别记录:从(i, j)位置回溯时,flag[i][j]需要重新设置为0,否则当输入为类似[[a,b,f],[b,e,g],[c,d,g]],"abcdebf"的情况时,由于某个位置(x, y)的flag没有重设为0,导致新的搜索路径无法到达(x, y),会出现判断错误。

Part2. 其他做法

递归方式

该做法是书上提供的,思路相似,但代码是递归写法,比我自己的简洁许多。我的flag数组在功能上和书中的visited标记类似,均是标记一个位置的搜索情况,但书中采用递归写法,于是不需要记录具体是向哪个方向搜索过,以此用左、上、右、下四个位置调用函数进行递归即可。另外,由于采用递归写法,当前搜索信息无需和我的代码一样用path_rowId、path_colId等变量记录位置。从书中的代码我感受到的是“更加简洁”。

Part3. 心得体会

  • 此题目是我目前调试时间最长的一题,调试时想起本科数据结构课程上类似的作业题,但当时用的时间远没有现在长,代码编写也更流畅。
  • 我更加感受到每天练习编程找回感觉是很有必要的,现在做题目的能力已经退步不少,但经历这段时间的练习,已经感觉要比刚开始时有明显进步,为自己加油!
  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值