|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[449. 序列化和反序列化二叉搜索树](https://leetcode.cn/problems/serialize-and-deserialize-bst/solution/by-ac_oier-ncwn/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「前序遍历」、「BST」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。 |
| 10 | + |
| 11 | +设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。 |
| 12 | + |
| 13 | +编码的字符串应尽可能紧凑。 |
| 14 | + |
| 15 | +示例 1: |
| 16 | +``` |
| 17 | +输入:root = [2,1,3] |
| 18 | +
|
| 19 | +输出:[2,1,3] |
| 20 | +``` |
| 21 | +示例 2: |
| 22 | +``` |
| 23 | +输入:root = [] |
| 24 | +
|
| 25 | +输出:[] |
| 26 | +``` |
| 27 | + |
| 28 | +提示: |
| 29 | +* 树中节点数范围是 $[0, 10^4]$ |
| 30 | +* $0 <= Node.val <= 10^4$ |
| 31 | +* 题目数据 保证 输入的树是一棵二叉搜索树。 |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +### BST 特性(前序遍历) |
| 36 | + |
| 37 | +实现上,我们可以忽略「BST」这一条件,使用「BFS」或者「直接充当满二叉树」来序列化和反序列化。 |
| 38 | + |
| 39 | +但由于点的数量是 $1e4$,最坏情况下是当 BST 成链时,会有较大的空间浪费。 |
| 40 | + |
| 41 | +因此,一种较为紧凑的序列化/反序列化的方式是利用「前序遍历 + BST 特性」: |
| 42 | + |
| 43 | +* 序列化:对 BST 进行「前序遍历」,并跳过空节点,节点值通过 `,` 进行分割,假设最终序列化出来的字符串是 `s`。 |
| 44 | + 之所以使用「前序遍历」是为了方便反序列化:首先对于某个子树而言,其必然是连续存储,也就是必然能够使用 $s[l,r]$ 所表示处理,同时首位元素必然是该子树的头结点; |
| 45 | + |
| 46 | +* 反序列化:将 `s` 根据分隔符 `,` 进行分割,假设分割后数组 `ss` 长度为 $n$,那么 $ss[0, n - 1]$ 代表完整的子树,我们可以利用「二叉树」特性递归构建,设计递归函数 `TreeNode dfs2(int l, int r, Sring[] ss)`,其含义为利用 $ss[l, r]$ 连续段构造二叉树,并返回头结点: |
| 47 | + 1. $ss[l]$ 为头结点,其值为 $t$,在 $[l, r]$ 范围内找到第一个比 $t$ 大的位置 $j$: |
| 48 | + 2. $ss[l]$ 的左子树的所有值均比 $t$ 小,且在 `s` 中连续存储,我们可以递归处理 $[l + 1, j - 1]$ 构建左子树; |
| 49 | + 3. $ss[l]$ 的右子树的所有值均比 $t$ 大,且在 `s` 中连续存储,我们可以递归处理 $[j, r]$ 构建右子树。 |
| 50 | + |
| 51 | +代码: |
| 52 | +```Java |
| 53 | +public class Codec { |
| 54 | + public String serialize(TreeNode root) { |
| 55 | + if (root == null) return null; |
| 56 | + List<String> list = new ArrayList<>(); |
| 57 | + dfs1(root, list); |
| 58 | + int n = list.size(); |
| 59 | + StringBuilder sb = new StringBuilder(); |
| 60 | + for (int i = 0; i < n; i++) { |
| 61 | + sb.append(list.get(i)); |
| 62 | + if (i != n - 1) sb.append(","); |
| 63 | + } |
| 64 | + return sb.toString(); |
| 65 | + } |
| 66 | + void dfs1(TreeNode root, List<String> list) { |
| 67 | + if (root == null) return ; |
| 68 | + list.add(String.valueOf(root.val)); |
| 69 | + dfs1(root.left, list); |
| 70 | + dfs1(root.right, list); |
| 71 | + } |
| 72 | + public TreeNode deserialize(String s) { |
| 73 | + if (s == null) return null; |
| 74 | + String[] ss = s.split(","); |
| 75 | + return dfs2(0, ss.length - 1, ss); |
| 76 | + } |
| 77 | + TreeNode dfs2(int l, int r, String[] ss) { |
| 78 | + if (l > r) return null; |
| 79 | + int j = l + 1, t = Integer.parseInt(ss[l]); |
| 80 | + TreeNode ans = new TreeNode(t); |
| 81 | + while (j <= r && Integer.parseInt(ss[j]) <= t) j++; |
| 82 | + ans.left = dfs2(l + 1, j - 1, ss); |
| 83 | + ans.right = dfs2(j, r, ss); |
| 84 | + return ans; |
| 85 | + } |
| 86 | +} |
| 87 | +``` |
| 88 | +* 时间复杂度:令节点数量为 $n$,复杂度为 $O(n)$ |
| 89 | +* 空间复杂度:$O(n)$ |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +### 最后 |
| 94 | + |
| 95 | +这是我们「刷穿 LeetCode」系列文章的第 `No.449` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 96 | + |
| 97 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 98 | + |
| 99 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 100 | + |
| 101 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 102 | + |
0 commit comments