1252简单. 奇数值单元格的数目
题意
给你一个 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 <= 501 <= indices.length <= 1000 <= ri < m0 <= ci < n
进阶: 你可以设计一个时间复杂度为 O(n + m + indices.length) 且仅用 O(n + m) 额外空间的算法来解决此问题吗?
因为这道题看一眼就知道暴力能解决,所以为了提升难度,直接上进阶版,控制时间和空间范围。
AC代码
👀Java版本:这个代码版本和思路也是经过推敲出来的
class Solution {
public int oddCells(int m, int n, int[][] indices) {
int[] rows= new int[m];
int[] cols = new int[n];
int len = indices.length;
for(int i=0;i<len;i++){
rows[indices[i][0]]++;
cols[indices[i][1]]++;
}
int row=0,col=0;
for(int i=0;i<m;i++) if((rows[i]&1)==1) row++;
for(int j=0;j<n;j++) if((cols[j]&1)==1) col++;
return (n-col)*row+(m-row)*col;
}
}
👀C++版本:
class Solution {
public:
int oddCells(int n, int m, vector<vector<int>>& indices) {
int ret = 0;
std::vector<int> rows(n),cols(m);
for(auto &idx : indices)
++rows[idx[0]],++cols[idx[1]];
for(auto &row : rows){
for(auto &col : cols)
ret += ((row+col) & 1);
}
return ret;
}
};
分析
🍚暴力版思路:
新建一个m行n列的二维矩阵,然后遍历indeces,把对应的每一行和每一列上的元素进行自加,最后再遍历二维数组,查找数值为奇数的即可,或者为了降低内存消耗量,可以把整型数组换成boolean数组,每次rec[i][j]=!rec[i][j]取反即可,最后判断rec[i][j]为true的点的个数即可,如果数据小的话,直接暴力应该是没问题的,如果数据量较大的话,可能就会出现超时的问题或者OOM的问题。
🍖进阶版思路:
按照进阶版的要求,内存占用为m+n,所以我们定义了一个m行的一维数组和n列的一维数组,用于记录每次变化的时候对应的行数和列数变化量,为什么要用这两个数组一会用到,遍历indeces数组的时候,我们需要记录影响的行数和列数,遍历完之后,分别查看影响的行数和列数,是否因为收到影响为奇数的,因为如果受到影响是偶数的,不会对原数组有影响,分别站在行的方面和列的方面记录受影响的个数,然后用这个公式=(总列数-受影响的列数)*受影响的行数+(总行数-受影响的行数)*受影响的列数,为什么要减去,因为站在行的角度,如果列的变化为奇数次,会抵消基数词变化的行数。
题解过程分析
- int[] rows= new int[m]; //定义一个记录影响的行数的数组
- int[] cols = new int[n]; //定义一个记录影响的列数的数组
- for(int i=0;i<len;i++){ rows[indices[i][0]]++; cols[indices[i][1]]++; } 遍历indeces的数组,分别记录影响的行数和列数。
- for(int i=0;i<m;i++) if((rows[i]&1)==1) row++; 记录受影响为奇数次的行数。
- for(int j=0;j<n;j++) if((cols[j]&1)==1) col++; 记录受影响为奇数次的列数。
- return (n-col)*row+(m-row)*col; 调用上边分析的公式。
图解过程
- 初始影响的行数和列数
- 筛选出受影响为奇数和偶数的行和列
- 站在行的角度
- 站在列的角度
所以公式就是根据上述推导出来的。
复杂度分析
- 时间复杂度:
O(n + m + indices.length) - 空间复杂度:
O(n + m)
总结
总体来说暴力就能解决的问题,仔细探索还能找到更精简的方法,就是是用公式去解决。 提交结果分析:时间上没问题了,其实我能改进的就是上边的数组。
- 改进点1: 可以使用无符号整型,因为所有的数组都是大于0的
- 改进点2: 还可以使用Map的键值对的格式,因为影响的行和列不是所有的都会受影响,所以用整型数组很多无用的空间被浪费。
- 改进点3: 把整形数组改为boolean数组,减少存储的容量,只要记录受影响的行数和列数即可,我这里就不再给出改进的代码,可以自己动手实现一下。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿