算法思想一:深度优先遍历DFS
解题思路:
深度优先搜索: 可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为 可行性剪枝
剪枝: 在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为 可行性剪枝
算法解析:
递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
终止条件: 当 i,j 行列索引越界 或 si + sj 数位和超出目标值 k 或 当前元素已访问过 时,返回 0 ,代表不计入可达解。
递推工作:
标记当前单元格 :将索引 (i, j) 存入辅助数组 res中,代表此单元格已被访问过。
搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
回溯返回值: 返回辅助数组 res 的长度
递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
终止条件: 当 i,j 行列索引越界 或 si + sj 数位和超出目标值 k 或 当前元素已访问过 时,返回 0 ,代表不计入可达解。
递推工作:
标记当前单元格 :将索引 (i, j) 存入辅助数组 res中,代表此单元格已被访问过。
搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
回溯返回值: 返回辅助数组 res 的长度
图解:
代码展示:
Python版本
class Solution: def movingCount(self, threshold, rows, cols): # write code here def dfs(i, j, k): # 边界 if not 0 <= i < rows&nbs***bsp;not 0 <= j < cols&nbs***bsp;(i, j) in res&nbs***bsp;(i % 10 + i // 10 + j % 10 + j // 10) > k: return 0 # 将访问节点加入可访问集中 res.add((i, j)) # 因为从(0, 0)开始,所以只要向下走或向右走就行 dfs(i+1, j, k) dfs(i, j+1, k) res = set() dfs(0, 0, threshold) return len(res)
复杂度分析:
时间复杂度 O(MN): M N分别为矩阵的长度和宽度,最差情况下,机器人遍历矩阵所有单元格,此时时间复杂度为 O(MN)。
空间复杂度 O(MN) : 最差情况下,res 内存储矩阵所有单元格的索引,使用 O(MN) 的额外空间。
空间复杂度 O(MN) : 最差情况下,res 内存储矩阵所有单元格的索引,使用 O(MN) 的额外空间。
算法思想二:广度优先遍历BFS
解题思路:
BFS/DFS : 两者目标都是遍历整个矩阵,不同点在于搜索顺序不同。DFS 是朝一个方向走到底,再回退,以此类推;BFS 则是按照“平推”的方式向前搜索。BFS 实现: 通常利用队列实现广度优先遍历。
算法解析:
初始化: 将机器人初始点 (0, 0)(0,0) 加入队列 queue ;
迭代终止条件: queue 为空。代表已遍历完所有可达解。
迭代工作:
单元格出队: 将队首单元格的 索引、数位和 弹出,作为当前搜索单元格。
判断是否跳过: 若 行列索引越界 或 数位和超出目标值 k 或当前元素已访问过 时,执行 continue 。
标记当前单元格 :将单元格索引 (i, j) 存入 visited 中,代表此单元格 已被访问过 。
单元格入队: 将当前元素的 下方、右方 单元格的 索引、数位和 加入 queue 。
返回值: 辅助数组 visited 的长度 len(visited) ,即可达解的数量。
图解:
代码展示:
JAVA版本
import java.util.Queue; import java.util.LinkedList; public class Solution { public int movingCount(int threshold, int rows, int cols) { boolean[][] visited = new boolean[rows][cols]; int res = 0; Queue<int[]> queue= new LinkedList<int[]>(); // 队列初始化 queue.add(new int[] { 0, 0, 0, 0 }); while(queue.size() > 0) { int[] x = queue.poll(); int i = x[0], j = x[1], si = x[2], sj = x[3]; if(i >= rows || j >= cols || threshold < si + sj || visited[i][j]) continue; visited[i][j] = true; res ++; queue.add(new int[] { i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj }); queue.add(new int[] { i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 }); } return res; } }
复杂度分析:
时间复杂度 O(MN): M N分别为矩阵的长度和宽度,最差情况下,机器人遍历矩阵所有单元格,此时时间复杂度为 O(MN)。
空间复杂度 O(MN) : 最差情况下,visited 内存储矩阵所有单元格的索引,使用 O(MN) 的额外空间。
空间复杂度 O(MN) : 最差情况下,visited 内存储矩阵所有单元格的索引,使用 O(MN) 的额外空间。