serialize和deserialize必须成对使用并约定统一遍历顺序与空节点标记;推荐先序遍历+逗号分隔,空节点用"null"显式标记,c++中deserialize需引用传递字符串及索引以避免解析错位。

serialize 和 deserialize 必须成对使用,且必须约定统一的遍历顺序和空节点标记;否则反序列化时会直接构造出结构错误的树,甚至崩溃。
用先序遍历 + 逗号分隔是最稳的方案
DFS 中只有先序(root→left→right)能天然保证:字符串第一个非空值就是根,后续子串可递归划分左右子树。中序、后序无法唯一还原结构,不推荐。
空节点必须用明确标记(如 "null" 或 "#"),不能省略或留空——否则解析时会错位。逗号 "," 是最安全的分隔符,避免数字含负号或多位数时解析歧义。
常见错误现象:
-
stoi遇到"#"直接抛异常 → 必须先判断再转换 - 字符串切片后没更新索引,导致重复消费或跳过节点 → 建议用引用传入
std::string& data并在函数内修改 - 递归反序列化时忘了跳过已消费的 token → 每次调用
deserializeCore后要移动指针或截断前缀
serialize 的递归实现要点
核心是边遍历边拼接,不用额外容器缓存。遇到空节点立即写 "null",非空则写 to_string(node->val),每个值后跟一个 ","。
示例片段:
string serialize(TreeNode* root) {
if (!root) return "null,";
return to_string(root->val) + "," + serialize(root->left) + serialize(root->right);
}
注意:serialize(root->left) 和 serialize(root->right) 必须都执行,不能短路;否则右子树缺失时结构信息丢失。
性能影响:每次字符串拼接产生新对象,深度大时有内存开销。生产环境可用 std::ostringstream 累积写入。
deserialize 必须用引用传递并原地推进索引
C++ 里如果传值或只传 string,递归过程中无法共享当前解析位置,极易错乱。正确做法是传 std::string& data 并配合一个可变索引(如 int& i)或用 stringstream 配合 getline。
关键步骤:
- 从当前位置提取下一个 token(直到遇到
',') - 若 token ==
"null",返回nullptr - 否则
new TreeNode(stoi(token)),再递归构建 left 和 right - 每消费一个 token,索引必须前进,不能靠字符串长度硬算
容易踩的坑:用 data.find(',') 然后 substr 再 erase —— 多次拷贝低效,且 erase 后原字符串变短,索引失效。
测试时务必验证结构而非仅值
只比对输出字符串是否相等没意义。要真正跑一遍 serialize → deserialize → serialize,看两次序列化结果是否一致;或者用 BFS 层序遍历打印两棵树,逐层比对节点存在性和值。
特别注意负数、0、大整数(如 INT_MIN)是否能无损 round-trip。C++ 的 stoi 对溢出不抛异常而是返回边界值,必要时改用 stol + 范围检查。
最常被忽略的一点:全局变量或静态索引在多线程或连续多次调用时未重置,导致第二次 deserialize 直接从中间开始解析。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











