题目详情:
给你一个 m x n 的二元矩阵 matrix ,且所有值被初始化为 0 。请你设计一个算法,随机选取一个满足 matrix[i][j] == 0 的下标 (i, j) ,并将它的值变为 1 。所有满足 matrix[i][j] == 0 的下标 (i, j) 被选取的概率应当均等。
尽量最少调用内置的随机函数,并且优化时间和空间复杂度。
实现 Solution 类:
Solution(int m, int n) 使用二元矩阵的大小 m 和 n 初始化该对象
int[] flip() 返回一个满足 matrix[i][j] == 0 的随机下标 [i, j] ,并将其对应格子中的值变为 1
void reset() 将矩阵中所有的值重置为 0
示例:
输入
[“Solution”, “flip”, “flip”, “flip”, “reset”, “flip”]
[[3, 1], [], [],[], [], []]输出
[null, [1, 0], [2, 0], [0, 0], null, [2, 0]]
解释 Solution solution = new Solution(3, 1); solution.flip(); // 返回 [1,
0],此时返回 [0,0]、[1,0] 和 [2,0] 的概率应当相同 solution.flip(); // 返回 [2, 0],因为
[1,0] 已经返回过了,此时返回 [2,0] 和 [0,0] 的概率应当相同 solution.flip(); // 返回 [0,
0],根据前面已经返回过的下标,此时只能返回 [0,0] solution.reset(); // 所有值都重置为 0
,并可以再次选择下标返回 solution.flip(); // 返回 [2, 0],此时返回 [0,0]、[1,0] 和 [2,0]
的概率应当相同
提示:
1 <= m, n <= 104
每次调用flip 时,矩阵中至少存在一个值为 0 的格子。
最多调用 1000 次 flip 和 reset 方法。
思路:
二维矩阵用一维数组表示也是老生常谈了,不做赘述了,就是将 i,j -> i*n + j做个一对一的映射。
转换成一维其实做法和前几天的随机数的思路是一模一样的。但是有个问题是,m和n都是10^410
4
,乘起来是10^810
8
,这样的数组维护起来是极其昂贵的。
注意到最多flip一千次,那么有没有办法可以只记录用过的数,并维护出上面我们想要的数组的样子呢?首先想到的就是哈希表了,既然没有数组去交换记录,是不是可以直接记录被用的数和他交换的数的映射呢?
假设我们的一维数组为 [0, 1, 2, 3, 4, 5],最后一个值为5;
第一次random,假如是3,我们下一次随机是想要[0, 1, 2, 4, 5]中取一个,将5填入3的位置,就像是做了一次3和5的交换,
数组变为 [0, 1, 2, 5, 4] (我们记录映射 3 -> 5)
第二次random,我们在0~4中取一个(数组坐标),假如不是3,我们做和上一次一样的操作;假如是3,那么我们这次随机出来的数就相当于是5,这个时候我们仍需要将3的映射更新,变为最新的最后一个数4,
这样数组就变为 [0, 1, 2, 4] (我们记录映射 3 -> 4),并不是真的做了这么个数组。
一直到最后将全部数组都可以随机出,被用过的数不会再出现,因为始终会取它们映射到的没被用过的数。
getOrDefault(x,y) : getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。斜体样式
class Solution {
int m, n, len;
Map<Integer, Integer> hm = new HashMap<>();
Random random = new Random();
public Solution(int m, int n) {
this.m = m;
this.n = n;
len = m * n - 1;
}
public int[] flip() {
int r = random.nextInt(len + 1);
int x = hm.getOrDefault(r, r);
hm.put(r, hm.getOrDefault(len, len));
len--;
return new int[]{x/n, x%n};
}
public void reset() {
len = m * n - 1;
hm.clear();
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(m, n);
* int[] param_1 = obj.flip();
* obj.reset();
*/