来源:力扣(LeetCode)
描述:
给你一个 m x n
的矩阵,最开始的时候,每个单元格中的值都是 0
。
另有一个二维索引数组 indices
,indices[i] = [ri, ci]
指向矩阵中的某个位置,其中 ri
和 ci
分别表示指定的行和列(从 0 开始编号)。
对 indices[i]
所指向的每个位置,应同时执行下述增量操作:
-
ri 行上的所有单元格,加 1 。
-
ci 列上的所有单元格,加 1 。
给你 m
、n
和 indices
。请你在执行完所有 indices
指定的增量操作后,返回矩阵中 奇数值单元格 的数目。
示例 1:
输入:m = 2, n = 3, indices = [[0,1],[1,1]]
输出:6
解释:最开始的矩阵是 [[0,0,0],[0,0,0]]。
第一次增量操作后得到 [[1,2,1],[0,1,0]]。
最后的矩阵是 [[1,3,1],[1,3,1]],里面有 6 个奇数。
示例 2:
输入:m = 2, n = 2, indices = [[1,1],[0,0]]
输出:0
解释:最后的矩阵是 [[2,2],[2,2]],里面没有奇数。
提示:
- 1 <= m, n <= 50
- 1 <= indices.length <= 100
- 0 <= ri < m
- 0 <= ci < n
方法一:直接模拟
直接使用使用一个 n × m
的矩阵来存放操作的结果,对于 indices
中的每一对 [ri, ci]
,将矩阵第 ri
行的所有数增加 1,第 ci
列的所有数增加 1。在所有操作模拟完毕后,我们遍历矩阵,得到奇数的数目。
代码:
class Solution {
public:
int oddCells(int m, int n, vector<vector<int>>& indices) {
int res = 0;
vector<vector<int>> matrix(m, vector<int>(n));
for (auto &index : indices) {
for (int i = 0; i < n; i++) {
matrix[index[0]][i]++;
}
for (int i = 0; i < m; i++) {
matrix[i][index[1]]++;
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] & 1) {
res++;
}
}
}
return res;
}
};
执行用时:4 ms, 在所有 C++ 提交中击败了71.37%的用户
内存消耗:7.9 MB, 在所有 C++ 提交中击败了16.94%的用户
复杂度分析
时间复杂度: O(q × (m+n) + m×n), 其中 q 表示数组 indices 的长度,m, n 为矩阵的行数与列数。遍历数组时,每次都需要更新矩阵中一行加一列,需要的时间为 O(q×(m+n)),最后还需要遍历矩阵,需要的时间为 O(m×n),总的时间复杂度为 O(q×(m+n)+m×n)。
空间复杂度: O(m×n),其中 m, n 为矩阵的行数与列数。需要存储矩阵的所有元素。
方法二:模拟空间优化
由于每次操作只会将一行和一列的数增加 1,因此我们可以使用一个行数组 rows
和列数组 cols
分别记录每一行和每一列被增加的次数。对于 indices
中的每一对 [ri, ci]
,我们将rows[ri]
和 cols[ci]
的值分别增加 1。
在所有操作完成后,我们可以计算出位置 (x, y)
位置的计数即为 rows[x] + cols[y]
。遍历矩阵,即可得到所有奇数的数目。
代码:
class Solution {
public:
int oddCells(int m, int n, vector<vector<int>>& indices) {
vector<int> rows(m), cols(n);
for (auto & index : indices) {
rows[index[0]]++;
cols[index[1]]++;
}
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if ((rows[i] + cols[j]) & 1) {
res++;
}
}
}
return res;
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:7.6 MB, 在所有 C++ 提交中击败了59.27%的用户
复杂度分析
时间复杂度: O(q + m × n), 其中 q 表示数组 indices 的长度,m, n 为矩阵的行数与列数。遍历数组时需要的时间为 O(q),最后还需要遍历矩阵,需要的时间为 O(m×n),因此总的时间复杂度为 O(q + m × n)。
空间复杂度:O(m + n) ,其中 m, n 为矩阵的行数与列数。需要存储矩阵的行数统计与列数统计。
方法三:计数优化
继续对方法二进行优化,矩阵中位于 (x, y)
位置的数为奇数,当且仅当 rows[x]
和 cols[y]
中恰好有一个为奇数,一个为偶数。设 rows
有 oddx 个奇数, cols
有 oddy 个奇数,因此对于 rows[x]
为偶数,那么在第 x 行有 oddy
个位置的数为奇数;对于rows[x]
为奇数,那么在第 x 行有 m − oddy
个位置的数为偶数。综上我们可以得到奇数的数目为 oddx × (n−oddy) + (m−oddx) × oddy
。
代码:
class Solution {
public:
int oddCells(int m, int n, vector<vector<int>>& indices) {
vector<int> rows(m), cols(n);
for (auto & index : indices) {
rows[index[0]]++;
cols[index[1]]++;
}
int oddx = 0, oddy = 0;
for (int i = 0; i < m; i++) {
if (rows[i] & 1) {
oddx++;
}
}
for (int i = 0; i < n; i++) {
if (cols[i] & 1) {
oddy++;
}
}
return oddx * (n - oddy) + (m - oddx) * oddy;
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:7.5 MB, 在所有 C++ 提交中击败了82.26%的用户
复杂度分析
时间复杂度:O(q + m + n) , 其中 q 表示数组 indices 的长度,m, n 为矩阵的行数与列数。
空间复杂度:O(m + n) ,其中 m, n 为矩阵的行数与列数。需要存储矩阵的行数统计与列数统计。
author:LeetCode-Solution