Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Index/栈.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
| [385. 迷你语法分析器](https://leetcode-cn.com/problems/mini-parser/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/mini-parser/solution/by-ac_oier-zuy6/) | 中等 | 🤩🤩🤩🤩🤩 |
| [591. 标签验证器](https://leetcode-cn.com/problems/tag-validator/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/tag-validator/solution/by-ac_oier-9l8z/) | 困难 | 🤩🤩🤩🤩 |
| [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/) | 困难 | 🤩🤩🤩🤩 |
| [735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/) | [LeetCode 题解链接](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/) | 中等 | 🤩🤩🤩🤩🤩 |
| [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/) | 中等 | 🤩🤩🤩🤩🤩 |
| [面试题 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/) | 简单 | 🤩🤩🤩 |
| [面试题 02.05. 链表求和](https://leetcode-cn.com/problems/sum-lists-lcci/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sum-lists-lcci/solution/by-ac_oier-v1zb/) | 中等 | 🤩🤩🤩 |
Expand Down
1 change: 1 addition & 0 deletions Index/模拟.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
| [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/) | 简单 | 🤩🤩🤩🤩 |
| [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/) | 困难 | 🤩🤩🤩🤩 |
| [729. 我的日程安排表 I](https://leetcode-cn.com/problems/my-calendar-i/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/my-calendar-i/solution/by-ac_oier-1znx/) | 中等 | 🤩🤩🤩 |
| [735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/) | [LeetCode 题解链接](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/) | 中等 | 🤩🤩🤩🤩🤩 |
| [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/) | 简单 | 🤩🤩🤩🤩🤩 |
| [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/) | 简单 | 🤩🤩🤩🤩 |
| [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/) | 简单 | 🤩🤩🤩🤩 |
Expand Down
103 changes: 97 additions & 6 deletions LeetCode/1251-1260/1252. 奇数值单元格的数目(简单).md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Tag : 「模拟」、「位运算」、「计数」

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

代码:
Java 代码:
```Java
class Solution {
public int oddCells(int m, int n, int[][] ins) {
Expand All @@ -78,6 +78,29 @@ class Solution {
}
}
```
TypeScript 代码:
```TypeScript
function oddCells(m: number, n: number, ins: number[][]): number {
const r = new Array<boolean>(m).fill(false), c = new Array<boolean>(n).fill(false)
let a = 0, b = 0
for (const [i, j] of ins) {
a += (r[i] = !r[i]) ? 1 : -1
b += (c[j] = !c[j]) ? 1 : -1
}
return a * (n - b) + (m - a) * b
};
```
Python 代码:
```Python3
class Solution:
def oddCells(self, m: int, n: int, ins: List[List[int]]) -> int:
r, c = [False] * m, [False] * n
for i, j in ins:
r[i] ^= 1
c[j] ^= 1
a, b = sum(r), sum(c)
return a * (n - b) + (m - a) * b
```
* 时间复杂度:构建计数数组的复杂度为 $O(m + n)$,统计奇数行和奇数列复杂度为 $O(l)$,其中 $l$ 为数组 `ins` 的长度,复杂度为 $O(m + n + l)$
* 空间复杂度:$O(m + n)$

Expand All @@ -87,9 +110,9 @@ class Solution {

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

当处理完所有的 `ins` 之后,可通过「遍历 $c1$ 的低 $m$ 位 + 遍历 $c2$ 的低 $n$ 位」来得到行数中奇数个数 $a$,列数中奇数个数 $b$,复杂度为 $O(m + n)$;也使用 `bitCount` 统计 `long` 二进制数中 $1$ 的个数(本质是分治操作),复杂度为 $O(\log{64})$。
当处理完所有的 `ins` 之后,可通过「遍历 $c1$ 的低 $m$ 位 + 遍历 $c2$ 的低 $n$ 位」来得到行数中奇数个数 $a$,列数中奇数个数 $b$,复杂度为 $O(m + n)$;也可以使用 `bitCount` 统计 `long` 二进制数中 $1$ 的个数(本质是分治操作),复杂度为 $O(\log{64})$。

代码:
Java 代码:
```Java
class Solution {
public int oddCells(int m, int n, int[][] ins) {
Expand All @@ -105,9 +128,7 @@ class Solution {
}
}
```

-

Java 代码:
```Java
class Solution {
public int oddCells(int m, int n, int[][] ins) {
Expand All @@ -121,11 +142,81 @@ class Solution {
}
}
```
TypeScript 代码:
```TypeScript
function oddCells(m: number, n: number, ins: number[][]): number {
const c1 = [0, 0], c2 = [0, 0]
for (const [i, j] of ins) {
c1[Math.ceil(i / 32)] ^= (1 << (i % 32))
c2[Math.ceil(j / 32)] ^= (1 << (j % 32))
}
let a = 0, b = 0
for (let i = 0; i < m; i++) a += (c1[Math.ceil(i / 32)] >> (i % 32) & 1)
for (let i = 0; i < n; i++) b += (c2[Math.ceil(i / 32)] >> (i % 32) & 1)
return a * (n - b) + (m - a) * b
};
```
Python 代码:
```Python3
class Solution:
def oddCells(self, m: int, n: int, ins: List[List[int]]) -> int:
c1, c2 = 0, 0
for i, j in ins:
c1 ^= (1 << i)
c2 ^= (1 << j)
a, b = 0, 0
for i in range(m):
a += (c1 >> i) & 1
for i in range(n):
b += (c2 >> i) & 1
return a * (n - b) + (m - a) * b
```
* 时间复杂度:处理所有的 `ins` 复杂度为 $O(l)$,其中 $l$ 为数组 `ins` 的长度;使用遍历方式统计奇数行和奇数列个数复杂度为 $O(m + n)$;使用 `bitCount` 操作统计二进制中 $1$ 个数,复杂度为 $O(\log{C})$,其中 $C = 64$ 为 `long` 二进制数长度,整体复杂度为 $O(l + m + n)$ 或 $O(l + \log{C})$
* 空间复杂度:$O(1)$

---

### 答疑

评论区有同学提到 `bitCount` 的复杂度问题,如下是 `Long.bitCount` 操作的源码:

```Java
public static int bitCount(long i) {
// HD, Figure 5-14
i = i - ((i >>> 1) & 0x5555555555555555L);
i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
i = i + (i >>> 8);
i = i + (i >>> 16);
i = i + (i >>> 32);
return (int)i & 0x7f;
}
```

这自然不是通过遍历统计位数的 $O(n)$ 做法,普遍会认为是 $O(1)$。

但上述操作目的是进行成组统计(分治),而能够这样写是因为默认了 `long` 是 $64$ 长度,因此严格意义来说这段代码复杂度是 $O(\log{64})$ 的。

作为对比,可以把 `Integer.bitCount` 的源码看一下:

```Java
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
```

统计原理如出一辙,而能够这样统计,是因为默认了 `int` 长度为 $32$,其分组统计所需要的操作次数也与二进制数的长度相关,因此复杂度为 $O(\log{32})$。

将上述两个 `bitCount` 视为 $O(1)$ 都是不对的。

---

### 最后

这是我们「刷穿 LeetCode」系列文章的第 `No.1252` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
Expand Down
91 changes: 90 additions & 1 deletion LeetCode/671-680/676. 实现一个魔法字典(中等).md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ magicDictionary.search("leetcoded"); // 返回 False

> **不了解「Trie / 字典树」的同学可以看前置 🧀:[字典树入门](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247488490&idx=1&sn=db2998cb0e5f08684ee1b6009b974089)。里面通过图例展示了字典树基本形态,以及提供了「数组实现」和「TrieNode 实现」两种方式,还有「数组大小估算方式」和「Trie 应用面」介绍**

代码:
Java 代码:
```Java
class MagicDictionary {
int N = 100 * 100, M = 26, idx = 0;
Expand Down Expand Up @@ -92,11 +92,100 @@ class MagicDictionary {
}
}
```
TypeScript 代码:
```TypeScript
class MagicDictionary {
N: number = 100 * 100; M: number = 26; idx: number = 0;
tr: number[][] = new Array<Array<number>>(this.N)
isEnd: boolean[] = new Array<boolean>(this.N * this.M).fill(false)
add(s: string): void {
let p = 0
for (let i = 0; i < s.length; i++) {
const u = s.charCodeAt(i) - 'a'.charCodeAt(0)
if (this.tr[p] == undefined) this.tr[p] = new Array<number>(this.M).fill(0)
if (this.tr[p][u] == 0) this.tr[p][u] = ++this.idx
p = this.tr[p][u]
}
this.isEnd[p] = true
}
query(s: string, idx: number, p: number, limit: number): boolean {
if (limit < 0) return false
if (idx == s.length) return this.isEnd[p] && limit == 0
const u = s.charCodeAt(idx) - 'a'.charCodeAt(0)
for (let i = 0; i < 26; i++) {
if (this.tr[p] == undefined || this.tr[p][i] == 0) continue
if (this.query(s, idx + 1, this.tr[p][i], i == u ? limit : limit - 1)) return true
}
return false
}
buildDict(ss: string[]): void {
for (const s of ss) this.add(s)
}
search(s: string): boolean {
return this.query(s, 0, 0, 1)
}
}
```
* 时间复杂度:`buildDict` 操作需要将所有字符存入 `Trie`,复杂度为 $\sum_{i = 0}^{n - 1} len(ss[i]])$;`search` 操作在不考虑 `limit` 以及字典树中最多只有 $100$ 具体方案所带来的剪枝效果的话,最坏情况下要搜索所有 $C^L$ 个方案,其中 $C = 26$ 为字符集大小,$L = 100$ 为搜索字符串的最大长度
* 空间复杂度:$O(N \times L \times C)$,其中 $N = 100$ 为存入 `Trie` 的最大方案数,$L = 100$ 为存入字符串的最大长度,$C = 26$ 为字符集大小

---

### 模拟

当然,利用数据范围只有 $100$,直接使用模拟也是可以的。

Java 代码:
```Java
class MagicDictionary {
String[] ss;
public void buildDict(String[] _ss) {
ss = _ss;
}
public boolean search(String str) {
for (String s : ss) {
int cnt = 0;
for (int i = 0; s.length() == str.length() && i < s.length() && cnt <= 1; i++) {
if (s.charAt(i) != str.charAt(i)) cnt++;
}
if (cnt == 1) return true;
}
return false;
}
}
```
TypeScript 代码:
```TypeScript
class MagicDictionary {
ss: string[]
buildDict(_ss: string[]): void {
this.ss = _ss
}
search(s: string): boolean {
for (const str of this.ss) {
let cnt = 0
for (let i = 0; str.length == s.length && i < s.length && cnt <= 1; i++) {
if (s.charAt(i) != str.charAt(i)) cnt++
}
if (cnt == 1) return true
}
return false
}
}
```
* 时间复杂度:`buildDict` 操作复杂度为 $O(1)$;`search` 操作复杂度为 $O(n \times L)$,其中 $n$ 为数组 `ss` 的长度,$L$ 为查询字符串的长度
* 空间复杂度:$O(n)$

---

### 加餐

1. 前置练习题 [可用 Trie 进阶的模拟题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492214&idx=1&sn=40fa070fe3b014873297f7ff740ba60f)

2. 另外一道 [结合 DFS 的 Trie 运用题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247492188&idx=1&sn=a1436d1ffe2b8200a36c3196ca1c7ed1)

---

### 最后

这是我们「刷穿 LeetCode」系列文章的第 `No.676` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
Expand Down
122 changes: 122 additions & 0 deletions LeetCode/731-740/735. 行星碰撞(中等).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
### 题目描述

这是 LeetCode 上的 **[735. 行星碰撞](https://leetcode.cn/problems/asteroid-collision/solution/by-ac_oier-p4qh/)** ,难度为 **中等**。

Tag : 「栈」、「模拟」



给定一个整数数组 `asteroids`,表示在同一行的行星。

对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向右移动,负表示向左移动)。每一颗行星以相同的速度移动。

找出碰撞后剩下的所有行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。

示例 1:
```
输入:asteroids = [5,10,-5]

输出:[5,10]

解释:10 和 -5 碰撞后只剩下 10 。 5 和 10 永远不会发生碰撞。
```
示例 2:
```
输入:asteroids = [8,-8]

输出:[]

解释:8 和 -8 碰撞后,两者都发生爆炸。
```
示例 3:
```
输入:asteroids = [10,2,-5]

输出:[10]

解释:2 和 -5 发生碰撞后剩下 -5 。10 和 -5 发生碰撞后剩下 10 。
```

提示:
* $2 <= asteroids.length <= 10^4$
* $-1000 <= asteroids[i] <= 1000$
* $asteroids[i] != 0$

---

### 模拟 + 栈

为了方便,我们令 `asteroids` 为 `ats`。

由于碰撞抵消总是从相邻行星之间发生,我们可以使用「栈」来模拟该过程。

从前往后处理所有的 $ats[i]$,使用栈存储当前未被抵消的行星,当栈顶元素方向往右,当前 $ats[i]$ 方向往左时,会发生抵消操作,抵消过程根据规则进行即可。

Java 代码:
```Java
class Solution {
public int[] asteroidCollision(int[] ats) {
Deque<Integer> d = new ArrayDeque<>();
for (int t : ats) {
boolean ok = true;
while (ok && !d.isEmpty() && d.peekLast() > 0 && t < 0) {
int a = d.peekLast(), b = -t;
if (a <= b) d.pollLast();
if (a >= b) ok = false;
}
if (ok) d.addLast(t);
}
int sz = d.size();
int[] ans = new int[sz];
while (!d.isEmpty()) ans[--sz] = d.pollLast();
return ans;
}
}
```
TypeScript 代码:
```TypeScript
function asteroidCollision(ats: number[]): number[] {
const stk: number[] = new Array<number>()
for (const t of ats) {
let ok: boolean = true
while (ok && stk.length > 0 && stk[stk.length - 1] > 0 && t < 0) {
const a = stk[stk.length - 1], b = -t
if (a <= b) stk.pop()
if (a >= b) ok = false
}
if (ok) stk.push(t)
}
return stk
};
```
Python 3 代码:
```Python3
class Solution:
def asteroidCollision(self, ats: List[int]) -> List[int]:
stk = []
for t in ats:
ok = True
while ok and stk and stk[-1] > 0 and t < 0:
a, b = stk[-1], -t
if a <= b:
stk.pop(-1)
if a >= b:
ok = False
if ok:
stk.append(t)
return stk
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(n)$

---

### 最后

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

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。