297. 二叉树的序列化与反序列化(困难)
题目
「序列化」把一棵二叉树变成字符串,方便存文件、传网络;「反序列化」再把这个字符串一对一地还原成最初那棵树。格式由你自定,唯一要求是:对任意合法 root,deserialize(serialize(root)) 重建出的树与原树等价。
因此字符串必须同时保留两类信息:节点值,以及树的形状(哪里是空、谁是谁的左子或右子)。
自造示例:树按层序写成 [4,2,null,1,3]——根是 4,左子是 2,2 的左、右分别是 1 和 3,根没有右子树。用本文约定可序列化为 "4,2,1,N,N,3,N,N,N"(N 表示空指针)。
约束:节点数 0 ~ 104;节点值 -1000 ~ 1000。判题页面常用层序数组展示输入输出(空节点写 null),但你的实现可以使用任意格式,最终只比较重建出来的树。
思路
一句话:选定一种遍历顺序,把“节点值”和“空指针”都当作 token 写进字符串;反序列化时按同一顺序,读一个 token 建一个节点。
最直白的做法是随手做一次前序遍历,把值拼起来。但只记值会丢失形状:[1,2](左子是 2)和 [1,null,2](右子是 2)前序都得到 1,2。字符串不唯一,自然无法唯一还原。
关键观察一:空指针也要写入。给两个例子补上空标记后,[1,2] → 1,2,N,N,N,[1,null,2] → 1,N,2,N,N,两者立刻可区分。空标记虽然“多写”,却把丢失的边界信息补了回来。
关键观察二:前序自带递归结构。记 S(空)="N",S(节点)=val,S(左),S(右)。解码就是这条公式的反向执行:读一个 token,是 N 就返回空;是数字就建节点,再递归建左、右。左右边界由空标记自动划出,不用另存下标。
层序(LeetCode 展示格式)同样可行,但前序递归与“先建左再建右”的建树递归几乎逐行对应,代码最短。故本文选前序 DFS + 空标记 + 分隔符。
解法
序列化和反序列化像一对互逆动作,共用同一个“先左后右”的递归骨架:
- serialize:前序遍历;空节点写
N,非空写val,token 之间加分隔符。 - deserialize:把字符串按分隔符切开,用一个“下一个待读”指针消费;读
N返回空,读数字就建节点,再依次递归构造左、右。
用自造树 [4,2,null,1,3] 走一遍反序列化过程:
| 步 | 读到 | 动作 | 剩余 token |
|---|---|---|---|
| 1 | 4 | 建根 4,先处理左子树 | 2,1,N,N,3,N,N,N |
| 2 | 2 | 成为 4 的左子,先处理它的左子树 | 1,N,N,3,N,N,N |
| 3 | 1 | 成为 2 的左子,接着补它两个空子 | N,N,3,N,N,N |
| 4 | N | 1 的左子为空,转向 1 的右子 | N,3,N,N,N |
| 5 | N | 1 的右子为空,返回去建 2 的右子树 | 3,N,N,N |
| 6 | 3 | 成为 2 的右子,接着补它两个空子 | N,N,N |
| 7~9 | N,N,N | 依次补 3 的左右空子、根 4 的右空子树 | 空 |
C++ 用空格分隔,istringstream >> token 天然按空白切分;Python 用逗号分隔 + iter() 连读,避免 pop(0) 的 O(n) 开销。
下图把上面 2 个步骤串成流程,并逐步演示:


C++
class Codec {
public:
string serialize(TreeNode* root) {
ostringstream out;
encode(root, out);
return out.str();
}
TreeNode* deserialize(string data) {
istringstream in(data);
return decode(in);
}
private:
void encode(TreeNode* root, ostringstream& out) {
if (!root) {
out << "N ";
return;
}
out << root->val << ' ';
encode(root->left, out);
encode(root->right, out);
}
TreeNode* decode(istringstream& in) {
string token;
if (!(in >> token)) {
return nullptr; // 空串兜底
}
if (token == "N") {
return nullptr;
}
TreeNode* node = new TreeNode(stoi(token));
node->left = decode(in);
node->right = decode(in);
return node;
}
};
还没有评论,来说两句?