题目描述
解题思路
这一题与上一题有点相似,都是用递归DFS的思想
剑指 Offer 12. 矩阵中的路径
不同点在于,上一题是求一条与字符串配对的通路,这一题是求可达区域。
求通路的话,可以是由中间某个点开始配对,往上下左右四个方向深搜继续寻找配对的,所以深搜的话四个方向都得走。
那求可达区域呢?
如果从(0,0)
作为入口深搜,根据可达解的结构和连通性,仅通过向右和向下移动,访问所有可达解 。退一步讲,就是遍历一个矩阵中的所有元素,如果从某一个顶点出发(左上角,右上角,左下角,右下角),仅通过向两个方向进行深搜便可完成。
图片来源:
作者:jyd
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/solution/mian-shi-ti-13-ji-qi-ren-de-yun-dong-fan-wei-dfs-b/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
而此题要求求可达区域的总点数,类似于求二叉树的结点总数(dfs(left)+dfs(right)+1
左子树结点数+右子树结点数+本身)
或者求二叉树的深度,比较两者中的最大值再加本身剑指 Offer 55 - I. 二叉树的深度( (left>right?left:right)+1
, 笔记剑指刷题日记剑指 剑指 Offer 55 - I. 二叉树的深度。
只不过二叉树访问结点时不需要标记,因为一个结点只能有一个父亲结点。但是因为在这里是矩阵,某一个结点的右边"儿子"可能是某一个结点的下边"儿子",所以需要标记,否则可能就会重复计数。由于某一个结点最多访问一次,要么可以进入,要么不可以进入,因此不需要跟上一题一样撤销标记,上一题需要撤销标记的原因是因为某一结点可以被访问多次,可以详见上一题的笔记剑指刷题日记剑指 剑指 Offer 12. 矩阵中的路径)。
解答代码如下:
class Solution {
public:
int movingCount(int m, int n, int k) {
vector<vector<int>> visit(m, vector<int>(n,0));
return dfs(visit, m,n,0, 0, k);
}
int dfs(vector<vector<int>> &visit, int m, int n, int x, int y, int k) // 改变vector的值,传入引用
{
if(x>=m || y>=n || visit[x][y] || (x/10+x%10+y/10+y%10)>k) // 注意边界值是等于行数或者列数,不要减1
return 0;
// 能执行以下语句说明,能够进入坐标为(x,y)的格子
// 标记
visit[x][y] = 1;
int down = dfs(visit, m,n,x+1, y, k);
int right = dfs(visit, m,n,x, y+1, k);
// 不需要解除标记,所有的格子最多访问一次,不像上一题可能要访问多次
return down+right+1;
}
};
猜想: 矩阵的顶点是否可以当作根节点?(中间的元素可以上下左右四个方向,但是顶点就两个)然后它只能往两个方向走,另外两个方向不就可以当作它的"左右子树"了?
仔细看看这几局,是不是像极了二叉树的先序遍历(访问当前结点->遍历左子树->遍历右子树)?
只不过二叉树访问结点时不需要标记,因为一个结点只能有一个父亲结点。但是因为在这里是矩阵,某一个结点的右边"儿子"可能是某一个结点的下边"儿子",所以访问当前结点的操作就变成了标记当前结点,表示已访问过。