文章目录
- 1. 题目来源
- 2. 题目说明
- 3. 题目解析
- 方法一:层序遍历+迭代解法
- 不好的解法1
- 不好的解法2
- 成功版
- 方法二:先序遍历+递归解法
1. 题目来源
链接:序列化二叉树
来源:LeetCode——《剑指-Offer》专项
2. 题目说明
请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
你可以将以下二叉树:
1 / \ 2 3/ \4 5
序列化为 “[1,2,3,null,null,4,5]”
3. 题目解析
方法一:层序遍历+迭代解法
这是一道 Hard
题,思路还是很清楚的,难就难在了字符串处理以及最后的测试用例 47、48 的数据接收上了。
- 使用层序遍历序列化二叉树。层次遍历是不能唯一确定一颗二叉树的。 若遇到一个节点空节点用
'#'
表示。每个节点后面添加一个','
代表结束,则可以唯一确定一颗二叉树。 - 上述二叉树层次序列化得到
1,2,3,#,#,4,5,#,#,#,#,
- 然后根据该序列反序列化。 (注意判断负数的情况)
说说我遇到的坑点吧:
- 首先在序列化的时候,不能直接采用
string
的push_back
方法,实在是过不去测试用例 47、48,可以采用+=
字符串的形式,也可以采用字符流的形式 - 不能仅仅采用一种标记层次序列化的方式,即不能仅添加
#
来分割字符串,这样做没办法唯一确定一颗二叉树,当树的节点值val
大于 10 时,将没有办法直接正常的取出val
值,因为#12#
产生歧义,不知道到底是1、2
还是12
- 节点值为负数的情况需要处理,这个确实一开始给忽略掉了
- 最末尾的
#
需要去掉,这个很容易想得到
基本上大点的坑点都在上面提示到了,果然 Hard
就不一般…一道简单的 dfs、bfs
硬是把我整的没脾气…
不好的解法1
思路太年轻,写的很爽,过不了…在这里是将整形 val
转为了 char
类型,所以在 string
取的话按位取也能直接取出来。
参见代码如下:
// 没 AC,样例 47/48 过不去
// [1,null,2,null,3,null,4,null,5,null,6,null,...,999,null,1000] 单支树,IO极其密集class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {string s;queue<TreeNode*> q;if (root) q.push(root);while (!q.empty()) {TreeNode *t = q.front(); q.pop();if (t) {s.push_back(t->val); // 过不去,string 爆掉了q.push(t->left);q.push(t->right);} else {s.push_back('#');}}return s;}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {int cnt = data.find_last_not_of('#');if (cnt != std::string::npos)data.erase(cnt + 1);elsedata.clear(); if (data.empty()) return nullptr;queue<TreeNode*> q;string val(data);TreeNode *res = new TreeNode(val[0]), *cur = res;q.push(cur);int i = 1;while (!q.empty()) {TreeNode *t = q.front(); q.pop();if (i > val.size() - 1) break;if (val[i] != '#') {cur = new TreeNode(val[i]);q.push(cur);t->left = cur;++i;}else {++i;}if (i > val.size() - 1) break;if (val[i] != '#') {cur = new TreeNode(val[i]);q.push(cur);t->right = cur;++i;}else {++i;}}return res;}
};
不好的解法2
改成字符串流进行操作,能够正常读取测试用例 47 了,这样按字符读入,就将整数 123
拆成了 1
2
3
,这三个字符,在后续 string [ ]
操作就不能输出完整的整数,也是报错了。即便对字符串进行处理,但由于字符串中仅添加 #
来分割字符串,没办法唯一确定一颗二叉树,当树的节点值 val
大于 10 时,将没有办法直接正常的取出 val
值,因为 #12#
产生歧义,不知道到底是 1、2
还是 12
,所以需要考虑唯一确定一颗二叉树!
class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {queue<TreeNode*> q;q.push(root);stringstream ss;while (!q.empty()) {root = q.front();q.pop();if (root) {q.push(root->left);q.push(root->right);ss << root->val;}else {ss << '#';}}string data = ss.str();return data;}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {int cnt = data.find_last_not_of('#');if (cnt != std::string::npos)data.erase(cnt + 1);elsedata.clear(); if (data.empty()) return nullptr;queue<TreeNode*> q;string val(data);TreeNode *res = new TreeNode(val[0] -'0'), *cur = res;q.push(cur);int i = 1;while (!q.empty()) {TreeNode *t = q.front(); q.pop();if (i > val.size() - 1) break;if (val[i] != '#' && i < val.size() - 1) {string tmp;while (i < val.size() && val[i] != '#' ) {tmp += val[i];++i;}cur = new TreeNode(stoi(tmp));q.push(cur);t->left = cur;}else {++i;}if (i > val.size() - 1) break;if (val[i] != '#') {string tmp;while (i < val.size() && val[i] != '#') {tmp += val[i];++i;}cur = new TreeNode(stoi(tmp));q.push(cur);t->right = cur;}else {++i;}}return res;}
};
成功版
解决掉上述问题,再解决负数问题,这题就搞定了!
参见代码如下:
// 执行用时 :8 ms, 在所有 C++ 提交中击败了84.14%的用户
// 内存消耗 :10 MB, 在所有 C++ 提交中击败了100.00%的用户/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Codec {
private:string encode(TreeNode* root) {if (!root) return "#,";string res = "";queue<TreeNode*> q;q.push(root);while (!q.empty()) {TreeNode *t = q.front(); q.pop();if (t) {res += to_string(t->val) + ",";q.push(t->left);q.push(t->right);} else {res += "#,";}}return res;}TreeNode* takeNum(const string& data, int& p) {if (data[p] == '#') {p += 2;return NULL;}bool isN = false;if (data[p] == '-') {isN = true;p++;}int val = 0;while (data[p] != ',') {val = val * 10 + (data[p] - '0');p++;}p++;if (isN) {val = -val;}return new TreeNode(val);}TreeNode* decode(const string& data) {if (data[0] == '#') return NULL;int p = 0;TreeNode *root = takeNum(data, p);queue<TreeNode*> q;q.push(root);while (!q.empty()) {TreeNode *t = q.front(); q.pop();TreeNode *l = takeNum(data, p);TreeNode *r = takeNum(data, p);if (l) q.push(l);if (r) q.push(r);t->left = l;t->right = r;}return root;}public:// Encodes a tree to a single string.string serialize(TreeNode* root) {string s = encode(root);return s;}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {TreeNode *root = decode(data);return root;}
};// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));
方法二:先序遍历+递归解法
借助输入和输出字符串流 istringstream
和 ostringstream
。
- 对于序列化,从根节点开始,如果节点存在,则将值存入输出字符串流,然后分别对其左右子节点递归调用序列化函数即可。
- 对于去序列化,先读入第一个字符,以此生成一个根节点,然后再对根节点的左右子节点递归调用去序列化函数即可
- 还是各种细节问题的处理,和上面差不多,就不在赘述,但是递归明显简洁许多
参见代码如下:
// 执行用时 :40 ms, 在所有 C++ 提交中击败了63.53%的用户
// 内存消耗 :26.5 MB, 在所有 C++ 提交中击败了100.00%的用户/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {ostringstream out;serialize(root, out);return out.str();}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {istringstream in(data);return deserialize(in);}
private:void serialize(TreeNode *root, ostringstream &out) {if (root) {out << root->val << ' ';serialize(root->left, out);serialize(root->right, out);} else {out << "# ";}}TreeNode* deserialize(istringstream &in) {string val;in >> val;if (val == "#") return nullptr;TreeNode *root = new TreeNode(stoi(val));root->left = deserialize(in);root->right = deserialize(in);return root;}
};// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));