LeetCode之距离顺序排列矩阵单元格

题目:给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C。
另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。
返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)
注意:曼哈顿距离并不是所谓的两点之间的距离,其计算公式为:|x1 - x2| + |y1 - y2|
示例:
在这里插入图片描述

方法一:直接排序

class Solution {
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        int[][] res=new int[R*C][2];
		int k=0;
		for(int i=0;i<R;i++) {
			for(int j=0;j<C;j++,k++) {
				res[k][0]=i;
				res[k][1]=j;
			}
		}
		Arrays.sort(res,(o1,o2)->(Math.abs(o1[0]-r0)+Math.abs(o1[1]-c0))-(Math.abs(o2[0]-r0)+Math.abs(o2[1]-c0)));
		return res;
    }
}

方法二:桶排序
注意到方法一中排序的时间复杂度太高。实际在枚举所有点时,我们可以直接按照哈曼顿距离分桶。这样我们就可以实现线性的桶排序

class Solution {
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        int maxDist = Math.max(r0, R - 1 - r0) + Math.max(c0, C - 1 - c0);
        List<List<int[]>> bucket = new ArrayList<List<int[]>>();
        for (int i = 0; i <= maxDist; i++) {//距离r0,c0的距离从0到maxDist共maxDist+1个桶
            bucket.add(new ArrayList<int[]>());
        }

        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                int d = dist(i, j, r0, c0);
                bucket.get(d).add(new int[]{i, j});//加入相应距离大小的桶里面
            }
        }
        int[][] ret = new int[R * C][];
        int index = 0;
        for (int i = 0; i <= maxDist; i++) {
            for (int[] it : bucket.get(i)) {
                ret[index++] = it;
            }
        }
        return ret;
    }

    public int dist(int r1, int c1, int r2, int c2) {
        return Math.abs(r1 - r2) + Math.abs(c1 - c2);
    }
}

方法三:几何法(类BFS)
思路及解法

我们也可以直接变换枚举矩阵的顺序,直接按照曼哈顿距离遍历该矩形即可。

注意到曼哈顿距离相同的位置恰好构成一个斜着的正方形边框,因此我们可以从小到大枚举曼哈顿距离,并使用循环来直接枚举该距离对应的边框。我们每次从该正方形边框的上顶点出发,依次经过右顶点、下顶点和左顶点,最后回到上顶点。这样即可完成当前层的遍历
在这里插入图片描述

class Solution {
    int[] dr = {1, 1, -1, -1};
    int[] dc = {1, -1, -1, 1};

    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        int maxDist = Math.max(r0, R - 1 - r0) + Math.max(c0, C - 1 - c0);
        int[][] ret = new int[R * C][];
        int row = r0, col = c0;
        int index = 0;
        ret[index++] = new int[]{row, col};
        for (int dist = 1; dist <= maxDist; dist++) {
            row--;
            for (int i = 0; i < 4; i++) {//斜正方形框上下左右顶点
                while ((i % 2 == 0 && row != r0) || (i % 2 != 0 && col != c0)) {
                    if (row >= 0 && row < R && col >= 0 && col < C) {
                        ret[index++] = new int[]{row, col};
                    }
                    row += dr[i];
                    col += dc[i];
                }
            }
        }
        return ret;
    }
}

方法四:BFS

  1. 可以把所有的坐标看作树的结点,距离相等的结点位于树的同一层
  2. 而对于每一层的结点,它们的距离 dist 可以分为行距离和列距离,且 rowDist + colDist = dist 必然成立
  3. 使 rowDist 从 0 到 dist 递增,colDist 相应有不同的值,可以得到不同的坐标:
  • 横坐标为:r0 - rowDist 或 r0 + rowDist
  • 纵坐标为:c0 - colDist 或 c0 + colDist
  • 注意特殊情况:rowDist 或 colDist 为 0 时,每组只有一个正确值
  1. 对步骤 3 中,所有在矩阵范围内的坐标进行记录
    注意:
  • 此解法不关心最大距离,只要步骤 4 中记录的结果达到 R * C 的数量就可以终止搜索
  • 此解法的时间复杂度是 O((R+C)^2),因为对每一种距离 dist,rowDist 都要进行从 0 开始递增到 dist 的遍历操作,而距离可能的最大值为 R + C
  • 此解法时间复杂度大于 O(R * C) 的原因是:每种距离可能产生多个不在矩阵内的坐标,但搜索算法必须依次检查予以排除
  • 理论上此解法并不比桶排序优秀,但是代码中极少创建额外的容器和对象,所以实际的运行效率不会太差
class Solution {
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        int[][] re = new int[R * C][2];
        int dist = 0;
        int cnt = 0;
        int[] factor = {-1, 1};
        while (cnt < R * C) {
            for (int rowDist = 0; rowDist <= dist; rowDist++) {
                int colDist = dist - rowDist;
                for (int i = 0; i < 2; i++) {
                    int row = r0 + factor[i] * rowDist;
                    for (int j = 0; j < 2; j++) {
                        int col = c0 + factor[j] * colDist;
                        if (row >= 0 && row < R && col >= 0 && col < C) {
                            re[cnt][0] = row;
                            re[cnt][1] = col;
                            cnt++;
                        }
                        if (colDist == 0) break;
                    }
                    if (rowDist == 0) break;
                }
            }
            dist++;
        }

        return re;
    }
}

方法五:stream

class Solution {
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        return IntStream.range(0, R).boxed()
            .flatMap(r -> IntStream.range(0, C).mapToObj(c -> new int[]{r, c}))
            .sorted((pos1, pos2) -> dist(pos1[0], pos1[1], r0, c0) - dist(pos2[0], pos2[1], r0, c0))
            .toArray(int[][]::new);
    }

    private int dist(int r, int c, int r0, int c0) {
        return Math.abs(r - r0) + Math.abs(c - c0);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值