Skip to content

Commit e42c71d

Browse files
Merge pull request SharingSource#596 from SharingSource/ac_oier
✨feat: add 735、676、1252
2 parents 73a402d + 21eb435 commit e42c71d

File tree

5 files changed

+311
-7
lines changed

5 files changed

+311
-7
lines changed

Index/栈.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
| [385. 迷你语法分析器](https://leetcode-cn.com/problems/mini-parser/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/mini-parser/solution/by-ac_oier-zuy6/) | 中等 | 🤩🤩🤩🤩🤩 |
1010
| [591. 标签验证器](https://leetcode-cn.com/problems/tag-validator/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/tag-validator/solution/by-ac_oier-9l8z/) | 困难 | 🤩🤩🤩🤩 |
1111
| [726. 原子的数量](https://leetcode-cn.com/problems/number-of-atoms/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-atoms/solution/gong-shui-san-xie-shi-yong-xiao-ji-qiao-l5ak4/) | 困难 | 🤩🤩🤩🤩 |
12+
| [735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/) | [LeetCode 题解链接](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/) | 中等 | 🤩🤩🤩🤩🤩 |
1213
| [1190. 反转每对括号间的子串](https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/solution/gong-shui-san-xie-shi-yong-shuang-duan-d-r35q/) | 中等 | 🤩🤩🤩🤩🤩 |
1314
| [面试题 03.01. 三合一](https://leetcode-cn.com/problems/three-in-one-lcci/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/three-in-one-lcci/solution/yi-ti-shuang-jie-er-wei-shu-zu-yi-wei-sh-lih7/) | 简单 | 🤩🤩🤩 |
1415
| [面试题 02.05. 链表求和](https://leetcode-cn.com/problems/sum-lists-lcci/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sum-lists-lcci/solution/by-ac_oier-v1zb/) | 中等 | 🤩🤩🤩 |

Index/模拟.md

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
| [720. 词典中最长的单词](https://leetcode-cn.com/problems/longest-word-in-dictionary/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-word-in-dictionary/solution/by-ac_oier-bmot/) | 简单 | 🤩🤩🤩🤩 |
8989
| [726. 原子的数量](https://leetcode-cn.com/problems/number-of-atoms/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-atoms/solution/gong-shui-san-xie-shi-yong-xiao-ji-qiao-l5ak4/) | 困难 | 🤩🤩🤩🤩 |
9090
| [729. 我的日程安排表 I](https://leetcode-cn.com/problems/my-calendar-i/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/my-calendar-i/solution/by-ac_oier-1znx/) | 中等 | 🤩🤩🤩 |
91+
| [735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/) | [LeetCode 题解链接](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/) | 中等 | 🤩🤩🤩🤩🤩 |
9192
| [747. 至少是其他数字两倍的最大数](https://leetcode-cn.com/problems/largest-number-at-least-twice-of-others/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/largest-number-at-least-twice-of-others/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-8179/) | 简单 | 🤩🤩🤩🤩🤩 |
9293
| [748. 最短补全词](https://leetcode-cn.com/problems/shortest-completing-word/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/shortest-completing-word/solution/gong-shui-san-xie-jian-dan-zi-fu-chuan-j-x4ao/) | 简单 | 🤩🤩🤩🤩 |
9394
| [762. 二进制表示中质数个计算置位](https://leetcode-cn.com/problems/prime-number-of-set-bits-in-binary-representation/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/prime-number-of-set-bits-in-binary-representation/solution/by-ac_oier-w50x/) | 简单 | 🤩🤩🤩🤩 |

LeetCode/1251-1260/1252. 奇数值单元格的数目(简单).md

+97-6
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Tag : 「模拟」、「位运算」、「计数」
6464

6565
当 $r[idx]$ 为 `True` 含义为第 $idx$ 行的累加值为奇数,否则为偶数。列数组 `c` 的统计规则同理。
6666

67-
代码:
67+
Java 代码:
6868
```Java
6969
class Solution {
7070
public int oddCells(int m, int n, int[][] ins) {
@@ -78,6 +78,29 @@ class Solution {
7878
}
7979
}
8080
```
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+
```
81104
* 时间复杂度:构建计数数组的复杂度为 $O(m + n)$,统计奇数行和奇数列复杂度为 $O(l)$,其中 $l$ 为数组 `ins` 的长度,复杂度为 $O(m + n + l)$
82105
* 空间复杂度:$O(m + n)$
83106

@@ -87,9 +110,9 @@ class Solution {
87110

88111
更进一步,我们可以使用两个 `long` 变量 $c1$ 和 $c2$ 来分别充当行和列的计数数组,当 $c1$ 的第 $k$ 位为 $1$,代表第 $k$ 行累加值为奇数,当 $c1$ 的第 $k$ 位为 $0$,代表第 $k$ 行累加值为偶数;$c2$ 的计数规则同理。而翻转二进制中的某一位可使用「异或」操作。
89112

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})$。
91114

92-
代码:
115+
Java 代码:
93116
```Java
94117
class Solution {
95118
public int oddCells(int m, int n, int[][] ins) {
@@ -105,9 +128,7 @@ class Solution {
105128
}
106129
}
107130
```
108-
109-
-
110-
131+
Java 代码:
111132
```Java
112133
class Solution {
113134
public int oddCells(int m, int n, int[][] ins) {
@@ -121,11 +142,81 @@ class Solution {
121142
}
122143
}
123144
```
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+
```
124174
* 时间复杂度:处理所有的 `ins` 复杂度为 $O(l)$,其中 $l$ 为数组 `ins` 的长度;使用遍历方式统计奇数行和奇数列个数复杂度为 $O(m + n)$;使用 `bitCount` 操作统计二进制中 $1$ 个数,复杂度为 $O(\log{C})$,其中 $C = 64$ 为 `long` 二进制数长度,整体复杂度为 $O(l + m + n)$ 或 $O(l + \log{C})$
125175
* 空间复杂度:$O(1)$
126176

127177
---
128178

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+
129220
### 最后
130221

131222
这是我们「刷穿 LeetCode」系列文章的第 `No.1252` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

LeetCode/671-680/676. 实现一个魔法字典(中等).md

+90-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ magicDictionary.search("leetcoded"); // 返回 False
5959

6060
> **不了解「Trie / 字典树」的同学可以看前置 🧀:[字典树入门](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247488490&idx=1&sn=db2998cb0e5f08684ee1b6009b974089)。里面通过图例展示了字典树基本形态,以及提供了「数组实现」和「TrieNode 实现」两种方式,还有「数组大小估算方式」和「Trie 应用面」介绍**
6161
62-
代码:
62+
Java 代码:
6363
```Java
6464
class MagicDictionary {
6565
int N = 100 * 100, M = 26, idx = 0;
@@ -92,11 +92,100 @@ class MagicDictionary {
9292
}
9393
}
9494
```
95+
TypeScript 代码:
96+
```TypeScript
97+
class MagicDictionary {
98+
N: number = 100 * 100; M: number = 26; idx: number = 0;
99+
tr: number[][] = new Array<Array<number>>(this.N)
100+
isEnd: boolean[] = new Array<boolean>(this.N * this.M).fill(false)
101+
add(s: string): void {
102+
let p = 0
103+
for (let i = 0; i < s.length; i++) {
104+
const u = s.charCodeAt(i) - 'a'.charCodeAt(0)
105+
if (this.tr[p] == undefined) this.tr[p] = new Array<number>(this.M).fill(0)
106+
if (this.tr[p][u] == 0) this.tr[p][u] = ++this.idx
107+
p = this.tr[p][u]
108+
}
109+
this.isEnd[p] = true
110+
}
111+
query(s: string, idx: number, p: number, limit: number): boolean {
112+
if (limit < 0) return false
113+
if (idx == s.length) return this.isEnd[p] && limit == 0
114+
const u = s.charCodeAt(idx) - 'a'.charCodeAt(0)
115+
for (let i = 0; i < 26; i++) {
116+
if (this.tr[p] == undefined || this.tr[p][i] == 0) continue
117+
if (this.query(s, idx + 1, this.tr[p][i], i == u ? limit : limit - 1)) return true
118+
}
119+
return false
120+
}
121+
buildDict(ss: string[]): void {
122+
for (const s of ss) this.add(s)
123+
}
124+
search(s: string): boolean {
125+
return this.query(s, 0, 0, 1)
126+
}
127+
}
128+
```
95129
* 时间复杂度:`buildDict` 操作需要将所有字符存入 `Trie`,复杂度为 $\sum_{i = 0}^{n - 1} len(ss[i]])$;`search` 操作在不考虑 `limit` 以及字典树中最多只有 $100$ 具体方案所带来的剪枝效果的话,最坏情况下要搜索所有 $C^L$ 个方案,其中 $C = 26$ 为字符集大小,$L = 100$ 为搜索字符串的最大长度
96130
* 空间复杂度:$O(N \times L \times C)$,其中 $N = 100$ 为存入 `Trie` 的最大方案数,$L = 100$ 为存入字符串的最大长度,$C = 26$ 为字符集大小
97131

98132
---
99133

134+
### 模拟
135+
136+
当然,利用数据范围只有 $100$,直接使用模拟也是可以的。
137+
138+
Java 代码:
139+
```Java
140+
class MagicDictionary {
141+
String[] ss;
142+
public void buildDict(String[] _ss) {
143+
ss = _ss;
144+
}
145+
public boolean search(String str) {
146+
for (String s : ss) {
147+
int cnt = 0;
148+
for (int i = 0; s.length() == str.length() && i < s.length() && cnt <= 1; i++) {
149+
if (s.charAt(i) != str.charAt(i)) cnt++;
150+
}
151+
if (cnt == 1) return true;
152+
}
153+
return false;
154+
}
155+
}
156+
```
157+
TypeScript 代码:
158+
```TypeScript
159+
class MagicDictionary {
160+
ss: string[]
161+
buildDict(_ss: string[]): void {
162+
this.ss = _ss
163+
}
164+
search(s: string): boolean {
165+
for (const str of this.ss) {
166+
let cnt = 0
167+
for (let i = 0; str.length == s.length && i < s.length && cnt <= 1; i++) {
168+
if (s.charAt(i) != str.charAt(i)) cnt++
169+
}
170+
if (cnt == 1) return true
171+
}
172+
return false
173+
}
174+
}
175+
```
176+
* 时间复杂度:`buildDict` 操作复杂度为 $O(1)$;`search` 操作复杂度为 $O(n \times L)$,其中 $n$ 为数组 `ss` 的长度,$L$ 为查询字符串的长度
177+
* 空间复杂度:$O(n)$
178+
179+
---
180+
181+
### 加餐
182+
183+
1. 前置练习题 [可用 Trie 进阶的模拟题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492214&idx=1&sn=40fa070fe3b014873297f7ff740ba60f)
184+
185+
2. 另外一道 [结合 DFS 的 Trie 运用题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492188&idx=1&sn=a1436d1ffe2b8200a36c3196ca1c7ed1)
186+
187+
---
188+
100189
### 最后
101190

102191
这是我们「刷穿 LeetCode」系列文章的第 `No.676` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/)** ,难度为 **中等**
4+
5+
Tag : 「栈」、「模拟」
6+
7+
8+
9+
给定一个整数数组 `asteroids`,表示在同一行的行星。
10+
11+
对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向右移动,负表示向左移动)。每一颗行星以相同的速度移动。
12+
13+
找出碰撞后剩下的所有行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。
14+
15+
示例 1:
16+
```
17+
输入:asteroids = [5,10,-5]
18+
19+
输出:[5,10]
20+
21+
解释:10 和 -5 碰撞后只剩下 10 。 5 和 10 永远不会发生碰撞。
22+
```
23+
示例 2:
24+
```
25+
输入:asteroids = [8,-8]
26+
27+
输出:[]
28+
29+
解释:8 和 -8 碰撞后,两者都发生爆炸。
30+
```
31+
示例 3:
32+
```
33+
输入:asteroids = [10,2,-5]
34+
35+
输出:[10]
36+
37+
解释:2 和 -5 发生碰撞后剩下 -5 。10 和 -5 发生碰撞后剩下 10 。
38+
```
39+
40+
提示:
41+
* $2 <= asteroids.length <= 10^4$
42+
* $-1000 <= asteroids[i] <= 1000$
43+
* $asteroids[i] != 0$
44+
45+
---
46+
47+
### 模拟 + 栈
48+
49+
为了方便,我们令 `asteroids``ats`
50+
51+
由于碰撞抵消总是从相邻行星之间发生,我们可以使用「栈」来模拟该过程。
52+
53+
从前往后处理所有的 $ats[i]$,使用栈存储当前未被抵消的行星,当栈顶元素方向往右,当前 $ats[i]$ 方向往左时,会发生抵消操作,抵消过程根据规则进行即可。
54+
55+
Java 代码:
56+
```Java
57+
class Solution {
58+
public int[] asteroidCollision(int[] ats) {
59+
Deque<Integer> d = new ArrayDeque<>();
60+
for (int t : ats) {
61+
boolean ok = true;
62+
while (ok && !d.isEmpty() && d.peekLast() > 0 && t < 0) {
63+
int a = d.peekLast(), b = -t;
64+
if (a <= b) d.pollLast();
65+
if (a >= b) ok = false;
66+
}
67+
if (ok) d.addLast(t);
68+
}
69+
int sz = d.size();
70+
int[] ans = new int[sz];
71+
while (!d.isEmpty()) ans[--sz] = d.pollLast();
72+
return ans;
73+
}
74+
}
75+
```
76+
TypeScript 代码:
77+
```TypeScript
78+
function asteroidCollision(ats: number[]): number[] {
79+
const stk: number[] = new Array<number>()
80+
for (const t of ats) {
81+
let ok: boolean = true
82+
while (ok && stk.length > 0 && stk[stk.length - 1] > 0 && t < 0) {
83+
const a = stk[stk.length - 1], b = -t
84+
if (a <= b) stk.pop()
85+
if (a >= b) ok = false
86+
}
87+
if (ok) stk.push(t)
88+
}
89+
return stk
90+
};
91+
```
92+
Python 3 代码:
93+
```Python3
94+
class Solution:
95+
def asteroidCollision(self, ats: List[int]) -> List[int]:
96+
stk = []
97+
for t in ats:
98+
ok = True
99+
while ok and stk and stk[-1] > 0 and t < 0:
100+
a, b = stk[-1], -t
101+
if a <= b:
102+
stk.pop(-1)
103+
if a >= b:
104+
ok = False
105+
if ok:
106+
stk.append(t)
107+
return stk
108+
```
109+
* 时间复杂度:$O(n)$
110+
* 空间复杂度:$O(n)$
111+
112+
---
113+
114+
### 最后
115+
116+
这是我们「刷穿 LeetCode」系列文章的第 `No.735` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
117+
118+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
119+
120+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
121+
122+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

0 commit comments

Comments
 (0)