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 @@ -4,6 +4,7 @@
| [240. 搜索二维矩阵 II](https://leetcode-cn.com/problems/search-a-2d-matrix-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/search-a-2d-matrix-ii/solution/gong-shui-san-xie-yi-ti-shuang-jie-er-fe-y1ns/) | 中等 | 🤩🤩🤩🤩 |
| [297. 二叉树的序列化与反序列化](https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof/solution/gong-shui-san-xie-er-cha-shu-de-xu-lie-h-n89a/) | 困难 | 🤩🤩🤩🤩🤩 |
| [437. 路径总和 III](https://leetcode-cn.com/problems/path-sum-iii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/path-sum-iii/solution/gong-shui-san-xie-yi-ti-shuang-jie-dfs-q-usa7/) | 中等 | 🤩🤩🤩🤩 |
| [449. 序列化和反序列化二叉搜索树](https://leetcode.cn/problems/serialize-and-deserialize-bst/) | [LeetCode 题解链接](https://leetcode.cn/problems/serialize-and-deserialize-bst/solution/by-ac_oier-ncwn/) | 中等 | 🤩🤩🤩🤩 |
| [563. 二叉树的坡度](https://leetcode-cn.com/problems/binary-tree-tilt/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/binary-tree-tilt/solution/gong-shui-san-xie-jian-dan-er-cha-shu-di-ekz4/) | 简单 | 🤩🤩🤩🤩 |
| [606. 根据二叉树创建字符串](https://leetcode-cn.com/problems/construct-string-from-binary-tree/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/construct-string-from-binary-tree/solution/by-ac_oier-i2sk/) | 简单 | 🤩🤩🤩🤩 |
| [653. 两数之和 IV - 输入 BST](https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst/solution/by-ac_oier-zr4o/) | 简单 | 🤩🤩🤩🤩 |
Expand Down
6 changes: 3 additions & 3 deletions LeetCode/1721-1730/1728. 猫和老鼠 II(困难).md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Tag : 「博弈论」、「动态规划」、「记忆化搜索」

当时在 [(题解) 913. 猫和老鼠](https://leetcode.cn/problems/cat-and-mouse/solution/gong-shui-san-xie-dong-tai-gui-hua-yun-y-0bx1/) 没能证出来更小 $K$ 值(回合数)的正确性,用的 $2n^2$ 做的 ,其余题解说 $2 n$ 合法,后来也被证实是错误的。

对于本题如果用相同的分析思路,状态数多达 $8 * 8 * 8 * 8 * 2 = 8192$ 种,题目很贴心调整了规则为 $1000$ 步以内为猫获胜,但证明 $K$ 的理论上界仍是困难(上次分析不出来,这次压根不想分析
对于本题如果用相同的分析思路,状态数多达 $8 \times 8 \times 8 \times 8 \times 2 = 8192$ 种,题目很贴心调整了规则为 $1000$ 步以内为猫获胜,但证明 $K$ 的理论上界仍是困难(上次分析不出来,这次压根不想分析

如果忽略 $K$ 值分析,代码还是很好写的:定义函数 `int dfs(int x, int y, int p, int q, int k)` 并配合记忆化搜索,其中鼠位于 $(x, y)$,猫位于 $(p, q)$,当前轮数为 $k$(由 $k$ 的奇偶性可知是谁的回合)。

Expand Down Expand Up @@ -151,8 +151,8 @@ class Solution {
}
}
```
* 时间复杂度:令 $n$ 和 $m$ 分别为矩阵的长宽,最长移动距离为 $L$,复杂度为 $O(n^2 * m^2 * 1000 * 4 * L)$
* 空间复杂度:$O(n^2 * m^2 * 1000)$
* 时间复杂度:令 $n$ 和 $m$ 分别为矩阵的长宽,最长移动距离为 $L$,复杂度为 $O(n^2 \times m^2 \times 1000 \times 4 \times L)$
* 空间复杂度:$O(n^2 \times m^2 \times 1000)$

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ class Solution {
}
}
```
* 时间复杂度:排序复杂度为 $O(n\log{n})$,循环 `n` 个事件,每次循环需要往回找一个事件,复杂度为 $O(n)$,并更新 `k` 个状态,复杂度为 $O(k)$,因此转移的复杂度为 $O(n * (n + k))$;总的复杂度为 $O(n * (n + k + \log{n}))$
* 空间复杂度:$O(n * k)$
* 时间复杂度:排序复杂度为 $O(n\log{n})$,循环 `n` 个事件,每次循环需要往回找一个事件,复杂度为 $O(n)$,并更新 `k` 个状态,复杂度为 $O(k)$,因此转移的复杂度为 $O(n \times (n + k))$;总的复杂度为 $O(n \times (n + k + \log{n}))$
* 空间复杂度:$O(n \times k)$

---

Expand Down Expand Up @@ -145,8 +145,8 @@ class Solution {
}
}
```
* 时间复杂度:排序复杂度为 $O(n\log{n})$,循环 `n` 个事件,每次循环需要往回找一个事件,复杂度为 $O(\log{n})$,并更新 `k` 个状态,复杂度为 $O(k)$,因此转移的复杂度为 $O(n * (\log{n} + k))$;总的复杂度为 $O(n * (k + \log{n}))$
* 空间复杂度:$O(n * k)$
* 时间复杂度:排序复杂度为 $O(n\log{n})$,循环 `n` 个事件,每次循环需要往回找一个事件,复杂度为 $O(\log{n})$,并更新 `k` 个状态,复杂度为 $O(k)$,因此转移的复杂度为 $O(n \times (\log{n} + k))$;总的复杂度为 $O(n \times (k + \log{n}))$
* 空间复杂度:$O(n \times k)$

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ $k$ 个数字的 平均值 为这些数字求和后再除以 $k$ 。

根据题意,我们需要构造长度为 $n$ 的序列 $ans$,使得 $ans$ 和 $rolls$ 并集的平均值为 $mean$。

由于最终的平均值 $mean$ 已知,我们可以直接算得两序列之和为 $t = (m + n) * mean$。
由于最终的平均值 $mean$ 已知,我们可以直接算得两序列之和为 $t = (m + n) \times mean$。

使用 $t$ 减去 $\sum_{i = 0}^{m}rolls[i]$ 可得 $\sum_{i = 0}^{n}ans[i]$。我们知道一个长度为 $n$ 的有效序列的元素和范围为 $[n, 6 * n]$(骰子编号为 $[1, 6]$),根据 $\sum_{i = 0}^{m}rolls[i]$ 与 $[n, 6 * n]$ 关系进行分情况讨论:
使用 $t$ 减去 $$\sum_{i = 0}^{m}rolls[i]$$ 可得 $$\sum_{i = 0}^{n}ans[i]$$。我们知道一个长度为 $n$ 的有效序列的元素和范围为 $[n, 6 \times n]$(骰子编号为 $[1, 6]$),根据 $\sum_{i = 0}^{m}rolls[i]$ 与 $[n, 6 \times n]$ 关系进行分情况讨论:

* 如果 $\sum_{i = 0}^{n}ans[i]$ 不落在 $[n, 6 * n]$ 范围内,无解,直接返回空数组;
* 如果 $\sum_{i = 0}^{n}ans[i]$ 落在 $[n, 6 * n]$ 范围内,有解,此时尝试构造一个合法的 $ans$ : 起始使用 $\left \lfloor \frac{\sum_{i = 0}^{n}ans[i]}{n} \right \rfloor$ 填充 $ans$,若 $\left \lfloor \frac{\sum_{i = 0}^{n}ans[i]}{n} \right \rfloor * n < \sum_{i = 0}^{n}ans[i]$,计算两者差异值 $d$,并尝试将 $d$ 分摊到前 $d$ 个 $ans[i]$ 上(该过程一定可以顺利进行)。
* 如果 $$\sum_{i = 0}^{n}ans[i]$$ 不落在 $[n, 6 \times n]$ 范围内,无解,直接返回空数组;

* 如果 $$\sum_{i = 0}^{m} rool[i]$$ 落在 $[n, 6 \times n]$ 范围内,有解,此时尝试构造一个合法的 $ans$ : 起始使用 $$\left \lfloor \frac{\sum_{i = 0}^{n}ans[i]}{n} \right \rfloor$$ 填充 $ans$,若 $$\left \lfloor \frac{\sum_{i = 0}^{n}ans[i]}{n} \right \rfloor \times n < \sum_{i = 0}^{n}ans[i]$$,计算两者差异值 $d$,并尝试将 $d$ 分摊到前 $d$ 个 $ans[i]$ 上(该过程一定可以顺利进行)。

代码:
```Java
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
### 题目描述

这是 LeetCode 上的 **[449. 序列化和反序列化二叉搜索树](https://leetcode.cn/problems/serialize-and-deserialize-bst/solution/by-ac_oier-ncwn/)** ,难度为 **中等**。

Tag : 「前序遍历」、「BST」



序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。

设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。

编码的字符串应尽可能紧凑。

示例 1:
```
输入:root = [2,1,3]

输出:[2,1,3]
```
示例 2:
```
输入:root = []

输出:[]
```

提示:
* 树中节点数范围是 $[0, 10^4]$
* $0 <= Node.val <= 10^4$
* 题目数据 保证 输入的树是一棵二叉搜索树。

---

### BST 特性(前序遍历)

实现上,我们可以忽略「BST」这一条件,使用「BFS」或者「直接充当满二叉树」来序列化和反序列化。

但由于点的数量是 $1e4$,最坏情况下是当 BST 成链时,会有较大的空间浪费。

因此,一种较为紧凑的序列化/反序列化的方式是利用「前序遍历 + BST 特性」:

* 序列化:对 BST 进行「前序遍历」,并跳过空节点,节点值通过 `,` 进行分割,假设最终序列化出来的字符串是 `s`。
之所以使用「前序遍历」是为了方便反序列化:首先对于某个子树而言,其必然是连续存储,也就是必然能够使用 $s[l,r]$ 所表示处理,同时首位元素必然是该子树的头结点;

* 反序列化:将 `s` 根据分隔符 `,` 进行分割,假设分割后数组 `ss` 长度为 $n$,那么 $ss[0, n - 1]$ 代表完整的子树,我们可以利用「二叉树」特性递归构建,设计递归函数 `TreeNode dfs2(int l, int r, Sring[] ss)`,其含义为利用 $ss[l, r]$ 连续段构造二叉树,并返回头结点:
1. $ss[l]$ 为头结点,其值为 $t$,在 $[l, r]$ 范围内找到第一个比 $t$ 大的位置 $j$:
2. $ss[l]$ 的左子树的所有值均比 $t$ 小,且在 `s` 中连续存储,我们可以递归处理 $[l + 1, j - 1]$ 构建左子树;
3. $ss[l]$ 的右子树的所有值均比 $t$ 大,且在 `s` 中连续存储,我们可以递归处理 $[j, r]$ 构建右子树。

代码:
```Java
public class Codec {
public String serialize(TreeNode root) {
if (root == null) return null;
List<String> list = new ArrayList<>();
dfs1(root, list);
int n = list.size();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(list.get(i));
if (i != n - 1) sb.append(",");
}
return sb.toString();
}
void dfs1(TreeNode root, List<String> list) {
if (root == null) return ;
list.add(String.valueOf(root.val));
dfs1(root.left, list);
dfs1(root.right, list);
}
public TreeNode deserialize(String s) {
if (s == null) return null;
String[] ss = s.split(",");
return dfs2(0, ss.length - 1, ss);
}
TreeNode dfs2(int l, int r, String[] ss) {
if (l > r) return null;
int j = l + 1, t = Integer.parseInt(ss[l]);
TreeNode ans = new TreeNode(t);
while (j <= r && Integer.parseInt(ss[j]) <= t) j++;
ans.left = dfs2(l + 1, j - 1, ss);
ans.right = dfs2(j, r, ss);
return ans;
}
}
```
* 时间复杂度:令节点数量为 $n$,复杂度为 $O(n)$
* 空间复杂度:$O(n)$

---

### 最后

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

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

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

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

Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ Tag : 「双指针」



给你一个有序数组 nums ,请你「原地」删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
给你一个有序数组 `nums` ,请你「原地」删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须「原地」修改输入数组 并在使用 $O(1)$ 额外空间的条件下完成。



说明:

为什么返回数值是整数,但输出的答案是数组呢?
Expand Down Expand Up @@ -50,9 +48,9 @@ for (int i = 0; i < len; i++) {

提示:

* 1 <= nums.length <= 3 * $10^4$
* -$10^4$ <= nums[i] <= $10^4$
* nums 已按升序排列
* $1 <= nums.length <= 3 \times 10^4$
* $-10^4 <= nums[i] <= 10^4$
* `nums` 已按升序排列

---

Expand All @@ -65,9 +63,7 @@ for (int i = 0; i < len; i++) {
* 由于是保留 `k` 个相同数字,**对于前 `k` 个数字,我们可以直接保留**
* 对于后面的任意数字,能够保留的前提是:**与当前写入的位置前面的第 `k` 个元素进行比较,不相同则保留**

举个🌰,我们令 `k=2`,假设有如下样例

[1,1,1,1,1,1,2,2,2,2,2,2,3]
举个🌰,我们令 `k=2`,假设有如下样例 $[1,1,1,1,1,1,2,2,2,2,2,2,3]$

1. 首先我们先让前 2 位直接保留,得到 1,1
2. 对后面的每一位进行继续遍历,能够保留的前提是与当前位置的前面 `k` 个元素不同(答案中的第一个 1),因此我们会跳过剩余的 1,将第一个 2 追加,得到 1,1,2
Expand Down