剑指 Offer II 107. 矩阵中的距离(542. 01 矩阵)
题目:
思路:
【1】深度搜索的方式:
【2】广度搜索的方式:
_ _ _ _ _ 1 _ _ 2 1 2 _ 2 1 2 3 2 1 2 3 _ 0 _ _ ==> 1 0 1 _ ==> 1 0 1 2 ==> 1 0 1 2 ==> 1 0 1 2 _ _ _ _ _ 1 _ _ 2 1 2 _ 2 1 2 3 2 1 2 3 _ _ _ _ _ _ _ _ _ 2 _ _ 3 2 3 _ 3 2 3 4 在广度优先搜索的每一步中,如果我们从矩阵中的位置 x 搜索到了位置 y,并且 y 还没有被搜索过,那么位置 y 离 0 的距离就等于位置 x 离 0 的距离加上 1。
【3】动态规划的方式:
代码展示:
动态规划的方式:
//时间8 ms击败84.93% //内存45.5 MB击败16.10% //时间复杂度:O(mn),其中 m 为矩阵行数,n 为矩阵列数。 //计算 dist 数组的过程中我们需要遍历四次矩阵,因此时间复杂度为 O(4mn)=O(mn)。 //空间复杂度:O(1),这里我们只计算额外的空间复杂度。 //除了答案数组以外,我们只需要常数空间存放若干变量。 class Solution { static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public int[][] updateMatrix(int[][] matrix) { int m = matrix.length, n = matrix[0].length; // 初始化动态规划的数组,所有的距离值都设置为一个很大的数 int[][] dist = new int[m][n]; for (int i = 0; i < m; ++i) { Arrays.fill(dist[i], Integer.MAX_VALUE / 2); } // 如果 (i, j) 的元素为 0,那么距离为 0 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (matrix[i][j] == 0) { dist[i][j] = 0; } } } // 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (i - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i - 1][j] + 1); } if (j - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i][j - 1] + 1); } } } // 只有 水平向左移动 和 竖直向下移动,注意动态规划的计算顺序 for (int i = m - 1; i >= 0; --i) { for (int j = 0; j < n; ++j) { if (i + 1 < m) { dist[i][j] = Math.min(dist[i][j], dist[i + 1][j] + 1); } if (j - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i][j - 1] + 1); } } } // 只有 水平向右移动 和 竖直向上移动,注意动态规划的计算顺序 for (int i = 0; i < m; ++i) { for (int j = n - 1; j >= 0; --j) { if (i - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i - 1][j] + 1); } if (j + 1 < n) { dist[i][j] = Math.min(dist[i][j], dist[i][j + 1] + 1); } } } // 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序 for (int i = m - 1; i >= 0; --i) { for (int j = n - 1; j >= 0; --j) { if (i + 1 < m) { dist[i][j] = Math.min(dist[i][j], dist[i + 1][j] + 1); } if (j + 1 < n) { dist[i][j] = Math.min(dist[i][j], dist[i][j + 1] + 1); } } } return dist; } }
//动态规划的常数优化 //时间7 ms击败86.83% //内存45.2 MB击败32.84% //时间复杂度:O(mn),其中 m 为矩阵行数,n 为矩阵列数。 //计算 dist 数组的过程中我们需要遍历两次矩阵,因此时间复杂度为 O(2mn)=O(mn)。 //空间复杂度:O(1),这里我们只计算额外的空间复杂度。 //除了答案数组以外,我们只需要常数空间存放若干变量。 class Solution { static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public int[][] updateMatrix(int[][] matrix) { int m = matrix.length, n = matrix[0].length; // 初始化动态规划的数组,所有的距离值都设置为一个很大的数 int[][] dist = new int[m][n]; for (int i = 0; i < m; ++i) { Arrays.fill(dist[i], Integer.MAX_VALUE / 2); } // 如果 (i, j) 的元素为 0,那么距离为 0 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (matrix[i][j] == 0) { dist[i][j] = 0; } } } // 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (i - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i - 1][j] + 1); } if (j - 1 >= 0) { dist[i][j] = Math.min(dist[i][j], dist[i][j - 1] + 1); } } } // 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序 for (int i = m - 1; i >= 0; --i) { for (int j = n - 1; j >= 0; --j) { if (i + 1 < m) { dist[i][j] = Math.min(dist[i][j], dist[i + 1][j] + 1); } if (j + 1 < n) { dist[i][j] = Math.min(dist[i][j], dist[i][j + 1] + 1); } } } return dist; } }
广度搜索的方式:
//时间12 ms击败68.34% //内存44.5 MB击败51.78% //时间复杂度:O(mn),其中 m 为矩阵行数,n 为矩阵列数,即矩阵元素个数。 //广度优先搜索中每个位置最多只会被加入队列一次,因此只需要 O(mn) 的时间复杂度。 //空间复杂度:O(mn),其中 m 为矩阵行数,n 为矩阵列数,即矩阵元素个数。 //除答案数组外,最坏情况下矩阵里所有元素都为 0,全部被加入队列中,此时需要 O(mn) 的空间复杂度。 class Solution { static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public int[][] updateMatrix(int[][] matrix) { int m = matrix.length, n = matrix[0].length; int[][] dist = new int[m][n]; boolean[][] seen = new boolean[m][n]; Queue<int[]> queue = new LinkedList<int[]>(); // 将所有的 0 添加进初始队列中 for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (matrix[i][j] == 0) { queue.offer(new int[]{i, j}); seen[i][j] = true; } } } // 广度优先搜索 while (!queue.isEmpty()) { int[] cell = queue.poll(); int i = cell[0], j = cell[1]; for (int d = 0; d < 4; ++d) { int ni = i + dirs[d][0]; int nj = j + dirs[d][1]; if (ni >= 0 && ni < m && nj >= 0 && nj < n && !seen[ni][nj]) { dist[ni][nj] = dist[i][j] + 1; queue.offer(new int[]{ni, nj}); seen[ni][nj] = true; } } } return dist; } }
深度搜索的方式:(但深度搜索的方式会出现超时)
class Solution { public int[][] updateMatrix(int[][] mat) { if (mat.length == 0 || mat[0].length ==0) return new int[][]{}; int[][] res = new int[mat.length][mat[0].length]; for (int i = 0; i < mat.length; i++){ for (int j = 0; j < mat[0].length; j++){ if (mat[i][j] == 0){ res[i][j] = 0; }else { res[i][j] = dfsFindShortPath(mat,i,j); } } } return res; } //上下左右 int[][] direct = {{-1,0},{1,0},{0,-1},{0,1}}; public int dfsFindShortPath(int[][] mat,int low,int col){ if (mat[low][col] == 0) return 0; for (int[] d : direct){ int t_low = low + d[0]; int t_col = col + d[1]; System.out.println(Arrays.toString(new int[]{t_low,t_col})); if (t_low >= 0 && t_col >= 0 && t_low < mat.length && t_col < mat[0].length ) //这里返回1的原因是因为本身所在的这个点不是0,需要移动一步才能到0的位置 if (mat[t_low][t_col] == 0) return 1; } int directMin = Integer.MAX_VALUE-1; for (int[] d : direct){ int t_low = low + d[0]; int t_col = col + d[1]; if (t_low >= 0 && t_col >= 0 && t_low < mat.length && t_col < mat[0].length ) directMin = Math.min(directMin,dfsFindShortPath(mat,t_low,t_col)); } return 1 + directMin; } }