leetcode1030
leetcode1030_距离顺序排列矩阵单元格
给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C。
另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。
返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)
实例:
输入:R = 2, C = 3, r0 = 1, c0 = 2
输出:[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2,2,3]
其他满足题目要求的答案也会被视为正确,例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。
暴力排序
这个题目很简单,就是表里二维坐标上的每个点,然后进行排序,很容易就想到了广度优先遍历,但我们这里先把最暴力的写法写出来,只需要把每个点保存到数组中,然后排序即可。
public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
/**
* 值得学习的一种二维数组的初始化方式!!! 实际上想要初始化一个二维数组不一定非要值得这个二维数组的shape
* 只需要知道最外层的大小即可! 里面可以继续重新new.
* 这里我们知道[0,0]到[R,C]一共有 R*C个点,只需要求出每个点对应的坐标并放入二维数组即可。
* 所以有[i*C + j]表示第个点的坐标,
* 然后sort
* 即
*/
int[][] ret = new int[R * C][]; // 可以这样初始化?
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
ret[i * C + j] = new int[]{i, j};
}
}
Arrays.sort(ret, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
return (Math.abs(a[0] - r0) + Math.abs(a[1] - c0)) - (Math.abs(b[0] - r0) + Math.abs(b[1] - c0));
}
});
return ret;
}
桶排序
上面这个方法是时间复杂度是O(R·Clog(R·C))。也就是快排的速度。但实际上这个排序的范围是有限的,也就是[0,0] ->[R,C]。实际在枚举所有点时,我们可以直接按照哈曼顿距离分桶。这样我们就可以实现线性的桶排序。
接下来就是复习桶排序:
桶排序利用函数的映射关系,减少了几乎所有的比较工作。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对桶中的少量数据做先进的比较排序即可。
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++) {
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);
}