@@ -64,7 +64,7 @@ Tag : 「模拟」、「位运算」、「计数」
64
64
65
65
当 $r[ idx] $ 为 ` True ` 含义为第 $idx$ 行的累加值为奇数,否则为偶数。列数组 ` c ` 的统计规则同理。
66
66
67
- 代码:
67
+ Java 代码:
68
68
``` Java
69
69
class Solution {
70
70
public int oddCells (int m , int n , int [][] ins ) {
@@ -78,6 +78,29 @@ class Solution {
78
78
}
79
79
}
80
80
```
81
+ TypeScript 代码:
82
+ ``` TypeScript
83
+ function oddCells(m : number , n : number , ins : number [][]): number {
84
+ const r = new Array <boolean >(m ).fill (false ), c = new Array <boolean >(n ).fill (false )
85
+ let a = 0 , b = 0
86
+ for (const [i, j] of ins ) {
87
+ a += (r [i ] = ! r [i ]) ? 1 : - 1
88
+ b += (c [j ] = ! c [j ]) ? 1 : - 1
89
+ }
90
+ return a * (n - b ) + (m - a ) * b
91
+ };
92
+ ```
93
+ Python 代码:
94
+ ``` Python3
95
+ class Solution :
96
+ def oddCells (self , m : int , n : int , ins : List[List[int ]]) -> int :
97
+ r, c = [False ] * m, [False ] * n
98
+ for i, j in ins:
99
+ r[i] ^= 1
100
+ c[j] ^= 1
101
+ a, b = sum (r), sum (c)
102
+ return a * (n - b) + (m - a) * b
103
+ ```
81
104
* 时间复杂度:构建计数数组的复杂度为 $O(m + n)$,统计奇数行和奇数列复杂度为 $O(l)$,其中 $l$ 为数组 ` ins ` 的长度,复杂度为 $O(m + n + l)$
82
105
* 空间复杂度:$O(m + n)$
83
106
@@ -87,9 +110,9 @@ class Solution {
87
110
88
111
更进一步,我们可以使用两个 ` long ` 变量 $c1$ 和 $c2$ 来分别充当行和列的计数数组,当 $c1$ 的第 $k$ 位为 $1$,代表第 $k$ 行累加值为奇数,当 $c1$ 的第 $k$ 位为 $0$,代表第 $k$ 行累加值为偶数;$c2$ 的计数规则同理。而翻转二进制中的某一位可使用「异或」操作。
89
112
90
- 当处理完所有的 ` ins ` 之后,可通过「遍历 $c1$ 的低 $m$ 位 + 遍历 $c2$ 的低 $n$ 位」来得到行数中奇数个数 $a$,列数中奇数个数 $b$,复杂度为 $O(m + n)$;也使用 ` bitCount ` 统计 ` long ` 二进制数中 $1$ 的个数(本质是分治操作),复杂度为 $O(\log{64})$。
113
+ 当处理完所有的 ` ins ` 之后,可通过「遍历 $c1$ 的低 $m$ 位 + 遍历 $c2$ 的低 $n$ 位」来得到行数中奇数个数 $a$,列数中奇数个数 $b$,复杂度为 $O(m + n)$;也可以使用 ` bitCount ` 统计 ` long ` 二进制数中 $1$ 的个数(本质是分治操作),复杂度为 $O(\log{64})$。
91
114
92
- 代码:
115
+ Java 代码:
93
116
``` Java
94
117
class Solution {
95
118
public int oddCells (int m , int n , int [][] ins ) {
@@ -105,9 +128,7 @@ class Solution {
105
128
}
106
129
}
107
130
```
108
-
109
- -
110
-
131
+ Java 代码:
111
132
``` Java
112
133
class Solution {
113
134
public int oddCells (int m , int n , int [][] ins ) {
@@ -121,11 +142,81 @@ class Solution {
121
142
}
122
143
}
123
144
```
145
+ TypeScript 代码:
146
+ ``` TypeScript
147
+ function oddCells(m : number , n : number , ins : number [][]): number {
148
+ const c1 = [0 , 0 ], c2 = [0 , 0 ]
149
+ for (const [i, j] of ins ) {
150
+ c1 [Math .ceil (i / 32 )] ^= (1 << (i % 32 ))
151
+ c2 [Math .ceil (j / 32 )] ^= (1 << (j % 32 ))
152
+ }
153
+ let a = 0 , b = 0
154
+ for (let i = 0 ; i < m ; i ++ ) a += (c1 [Math .ceil (i / 32 )] >> (i % 32 ) & 1 )
155
+ for (let i = 0 ; i < n ; i ++ ) b += (c2 [Math .ceil (i / 32 )] >> (i % 32 ) & 1 )
156
+ return a * (n - b ) + (m - a ) * b
157
+ };
158
+ ```
159
+ Python 代码:
160
+ ``` Python3
161
+ class Solution :
162
+ def oddCells (self , m : int , n : int , ins : List[List[int ]]) -> int :
163
+ c1, c2 = 0 , 0
164
+ for i, j in ins:
165
+ c1 ^= (1 << i)
166
+ c2 ^= (1 << j)
167
+ a, b = 0 , 0
168
+ for i in range (m):
169
+ a += (c1 >> i) & 1
170
+ for i in range (n):
171
+ b += (c2 >> i) & 1
172
+ return a * (n - b) + (m - a) * b
173
+ ```
124
174
* 时间复杂度:处理所有的 ` ins ` 复杂度为 $O(l)$,其中 $l$ 为数组 ` ins ` 的长度;使用遍历方式统计奇数行和奇数列个数复杂度为 $O(m + n)$;使用 ` bitCount ` 操作统计二进制中 $1$ 个数,复杂度为 $O(\log{C})$,其中 $C = 64$ 为 ` long ` 二进制数长度,整体复杂度为 $O(l + m + n)$ 或 $O(l + \log{C})$
125
175
* 空间复杂度:$O(1)$
126
176
127
177
---
128
178
179
+ ### 答疑
180
+
181
+ 评论区有同学提到 ` bitCount ` 的复杂度问题,如下是 ` Long.bitCount ` 操作的源码:
182
+
183
+ ``` Java
184
+ public static int bitCount(long i) {
185
+ // HD, Figure 5-14
186
+ i = i - ((i >>> 1 ) & 0x5555555555555555L );
187
+ i = (i & 0x3333333333333333L ) + ((i >>> 2 ) & 0x3333333333333333L );
188
+ i = (i + (i >>> 4 )) & 0x0f0f0f0f0f0f0f0fL ;
189
+ i = i + (i >>> 8 );
190
+ i = i + (i >>> 16 );
191
+ i = i + (i >>> 32 );
192
+ return (int )i & 0x7f ;
193
+ }
194
+ ```
195
+
196
+ 这自然不是通过遍历统计位数的 $O(n)$ 做法,普遍会认为是 $O(1)$。
197
+
198
+ 但上述操作目的是进行成组统计(分治),而能够这样写是因为默认了 ` long ` 是 $64$ 长度,因此严格意义来说这段代码复杂度是 $O(\log{64})$ 的。
199
+
200
+ 作为对比,可以把 ` Integer.bitCount ` 的源码看一下:
201
+
202
+ ``` Java
203
+ public static int bitCount(int i) {
204
+ // HD, Figure 5-2
205
+ i = i - ((i >>> 1 ) & 0x55555555 );
206
+ i = (i & 0x33333333 ) + ((i >>> 2 ) & 0x33333333 );
207
+ i = (i + (i >>> 4 )) & 0x0f0f0f0f ;
208
+ i = i + (i >>> 8 );
209
+ i = i + (i >>> 16 );
210
+ return i & 0x3f ;
211
+ }
212
+ ```
213
+
214
+ 统计原理如出一辙,而能够这样统计,是因为默认了 ` int ` 长度为 $32$,其分组统计所需要的操作次数也与二进制数的长度相关,因此复杂度为 $O(\log{32})$。
215
+
216
+ 将上述两个 ` bitCount ` 视为 $O(1)$ 都是不对的。
217
+
218
+ ---
219
+
129
220
### 最后
130
221
131
222
这是我们「刷穿 LeetCode」系列文章的第 ` No.1252 ` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
0 commit comments