Leetcode 519. Random Flip Matrix

Problem Explanation:

The problem at hand involves a 2D binary matrix of n_rows and n_cols initially filled with 0s. You are to write two functions, flip and reset.

The function flip() is to choose a cell with the value 0 at random, replace it with a value of 1 and then return the row and column indices of that cell. If there are no cells with the value 0, it should return an empty list.

The function reset() is to set all cell values back to 0.

The problem also specifies limitations of the number of rows and columns, which must be within 1 and 10000 (1 <= n_rows, n_cols <= 10000) and the indices of row and column (0 <= row.id < n_rows and 0 <= col.id < n_cols). Additionally, the total number of calls to flip() and reset() will not exceed 1000.

For example, input:[[2,3],[],[],[],[]] corresponds to a 2x3 matrix. The [] indicate calls to flip. Thus, the matrix looks like: 0 0 0 0 0 0 Calling flip four times might yield outputs like: [0,1], [1,2], [1,0], [1,1].

Solution Explanation:

The solution uses the approach of representing the entire 2D grid as one linear sequence, such that each cell from the original 2D matrix would be uniquely represented by a single index in the 1D sequence. The flip function will then select a random index within the total number of cells, then it will convert it back to 2D representations (row and col) to return. It also ensures the selected cell was not previously selected using set used to keep track of single cell indexes that were already selected.

The reset function is straightforward as it's resetting all the cells back to 0, which is achieved by making used set back to empty set.

For example (Python variable names used):

If n_rows = 2 and n_cols = 3, we have 6 total cells. total stores the count of all cells = n_rows * n_cols.

index first randomly selects a number between 0 and 5. It also increments this index until it lands on a number not present in used.

Following index selection, that index is added to used then converted to [row, col] representation and returned.

Code:

Python Solution:

1
2python
3import random
4
5class Solution:
6    def __init__(self, n_rows: int, n_cols: int):
7        self.n_rows = n_rows
8        self.n_cols = n_cols
9        self.grid_size = n_rows * n_cols
10        self.used_cells = set()
11
12    def flip(self):
13        
14        while True:
15            index = random.choice(range(self.grid_size))
16            if not index in self.used_cells:
17                self.used_cells.add(index)
18                # Converting 1D index to 2D(index/n_cols, index%n_cols)
19                return divmod(index, self.n_rows)
20
21    def reset(self):
22        self.used_cells.clear()
23

Java Solution:

1
2java
3
4public class Solution {
5    HashMap<Integer, Integer> map;
6    int rows, cols, total;
7    Random rnd;
8    
9    public Solution(int n_rows, int n_cols) {
10        map = new HashMap<>();
11        rnd = new Random();
12        rows=n_rows; cols = n_cols;
13        total=rows*cols;
14    }
15    
16    public int[] flip() {
17        int r = rnd.nextInt(total--);
18        int x= map.getOrDefault(r, r);
19        map.put(r, map.getOrDefault(total, total));
20        return new int[]{x / cols, x % cols};
21    }
22    
23    public void reset() {
24        map.clear();
25        total = rows*cols;
26    }
27}

JavaScript Solution:

1
2javascript
3
4var Solution = function(n_rows, n_cols) {
5    this.used = [];
6    this.total = n_rows * n_cols;
7    this.row = n_rows;
8    this.col = n_cols;
9};
10
11Solution.prototype.flip = function() {
12    let rand = parseInt(Math.random() * this.total);
13    this.total--;
14    let res = this.used[rand] == null ? rand : this.used[rand];
15    this.used[rand] = this.used[this.total] == null ? this.total : this.used[this.total];
16    return [Math.floor(res / this.col), res % this.col];
17};
18
19Solution.prototype.reset = function() {
20    this.used = [];
21    this.total = this.row * this.col;
22};

C++ Solution:

1
2c++
3class Solution {
4private:
5    unordered_set<int> used;
6    int rows,cols,total;
7public:
8    Solution(int n_rows, int n_cols) {
9        rows=n_rows;
10        cols=n_cols;
11        total=rows*cols;
12    }
13    
14    vector<int> flip() {
15        if(used.size()==total)return {};
16        int index=rand()%total;
17        while(used.count(index))
18            index=++index%total;
19        used.insert(index);
20        return { index/cols, index%cols};
21    }
22    
23    void reset() {
24        used.clear();
25    }
26};

C# Solution:

1
2csharp
3public class Solution {
4    int nr;
5    int nc;
6    int max;
7    HashSet<int> used;
8    Random rand;
9    
10    public Solution(int n_rows, int n_cols) {
11        this.nr = n_rows;
12        this.nc = n_cols;
13        this.used = new HashSet<int>();
14        this.max = nr*nc;
15        this.rand = new Random();
16    }
17    
18    public int[] Flip() {
19        int r;
20        do
21        {
22            r = rand.Next(max);
23        }
24        while (!used.Add(r));
25        return new []{ r / nc, r % nc };
26    }
27    
28    public void Reset() {
29        used.Clear();
30    }
31}

These code snippets show that the mentioned approach is feasible with different programming languages.Also, from a performance perspective, these solutions are within acceptable limits. With both the time complexity and space complexity being O(n), where n represents the total number of cells in the matrix, the solution can efficiently handle a large number of total calls to both flip and reset methods.

However, there are a couple of caveats to this approach:

  1. Maintaining all cells in one list takes more space than the input 2D matrix would. Yet, it balances out as it makes the flip operation more efficient by avoiding the need to scan through the grid, which would have been costlier in terms of time complexity.

  2. This approach does not guarantee an even distribution of 1s in the resulting matrix after multiple flip calls. The reason being, the cells are selected randomly in each flip operation so it could happen that some cells get selected more frequently as compared to others.

However, the approach fulfills the requirements of the problem by ensuring:

  1. Each flip operation selects a unique cell with value zero.

  2. The reset operation returns the matrix back to the original state.

Thus, despite the above caveats, the mentioned approaches provide practical and workable solutions for the given problem, that too with an acceptable performance. They serve as a useful paradigm in demonstrating how sometimes, converting a complex data structure like a 2D matrix into a simpler one can make solving problems easier and make the code more manageable and efficient.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫