(吊打二维表中的搜索问题) LeetCode刷题之二维表中进行搜索的问题

简介

在一个二维表中进行搜索是一种经典的题目,最常用的方法就是回溯法,是一种非常直观的方法。我在这篇文章中总结了解这种类型题目的三板斧:

步骤方法名
1构建visited数组,用来记录是否访问过当前结点
2构建四个方向对,用来控制前进方向
3搞清楚每一步是否满足题目条件(判别条件)

下面我将用两个剑指offer中的例子来进行说明。

13,机器人的运动范围

题目:

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

我们按照上面的三板斧来进行判断,首先看是否需要记录是否访问过当前结点,结果是显然的,不然会重复记录满足要求的位置,再看第二点,也是我觉得官方解题中比较好的点:vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
用一个pair数组来保存坐标i,j的移动,我看了真的是觉得非常直观,这样只需要遍历这个数组就能准确控制坐标的移动的四个方向,最后就是弄清楚判定条件:我们移动到一个点首先就是把当前位置的visited置为true,再看是否位数和是小于k的,如果不小于就直接回溯了,如果小于就向四个方向进行移动,需要判断是否超出了边界,是否先前访问过,然后进行递归,同时把记录个数的sum进行++。最后要看在回溯的时候是否有原始变量要进行回退(显然本题不需要,因为不需要记录路径,只需要记录个数)。

tips

在check函数里面调用getsum函数的时候可以 是可以使用完美转发的一个典型条件,但是此时需要转发的数据是一个平凡的数据类型,所以这里是否使用完美转发对性能没有影响。

getsum(forward<int>(i),j

解题代码

class Solution {
public:
    int movingCount(int m, int n, int k) {
        vector<vector<bool>> visited (m,vector<bool>(n,false));
        int sum = 0;
        int i = 0;
        int j = 0;
        check(visited,i,j,k,sum);
        return sum;
    }

    void check(vector<vector<bool>> &visited,int &i, int &j, int k,int &sum){
        if(getsum(i,j) > k){
            return;
        }
        visited[i][j] = true;
        ++sum;
        vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        for(const auto &dir : directions){
            int newi = i + dir.first, newj = j + dir.second;
            if (newi >= 0 && newi < visited.size() && newj >= 0 && newj < visited[0].size()){
                if (!visited[newi][newj]) {
                    check(visited,newi,newj,k,sum);
                }
            }
        }
    }

    int getsum(int i, int j){
        int sum = 0;
        while(i != 0){
            sum += i%10;
            i /= 10;
        }
        while(j != 0){
            sum += j%10;
            j /= 10;
        }
        return sum; 
    }
};

12 矩阵中的路径

题目

题目

解题思路

大家可以参考我上面给出的思路,想想这个题目应该怎么解。这个题目在进行移动的时候除了要考虑边界,还要考虑是否访问过,并且判断当前位置是否与给定单词的相应位置的字符一样。

解题代码

class Solution {
public:
    bool check(vector<vector<char>>& board, vector<vector<int>>& visited, int i, int j, string& s, int k) {
        if (board[i][j] != s[k]) {
            return false;
        } else if (k == s.length() - 1) {
            return true;
        }
        visited[i][j] = true;
        // 表示四个方向 方便进行计算
        vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        bool result = false;
        for (const auto& dir: directions) {
            int newi = i + dir.first, newj = j + dir.second;
            if (newi >= 0 && newi < board.size() && newj >= 0 && newj < board[0].size()) {
                // 判断是否有重复
                if (!visited[newi][newj]) {
                    bool flag = check(board, visited, newi, newj, s, k + 1);
                    if (flag) {
                        result = true;
                        break;
                    }
                }
            }
        }
        // 回溯
        visited[i][j] = false;
        return result;
    }

    bool exist(vector<vector<char>>& board, string word) {
        int h = board.size(), w = board[0].size();
        vector<vector<int>> visited(h, vector<int>(w));
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                bool flag = check(board, visited, i, j, word, 0);
                if (flag) {
                    return true;
                }
            }
        }
        return false;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强大的RGG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值