剑指 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;
    }
}

 

posted @ 2023-04-17 12:37  忧愁的chafry  阅读(12)  评论(0编辑  收藏  举报