题目
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
- 示例 1:
输入:
root = [1,2,3,null,null,4,5]
输出:[1,2,3,null,null,4,5]
- 示例 2:
输入:
root = []
输出:[]
- 示例 3:
输入:
root = [1]
输出:[1]
- 示例 4:
输入:
root = [1,2]
输出:[1,2]
- 提示:
树中结点数在范围 [0, 104] 内
-1000 <= Node.val <= 1000
注意:本题与主站 297 题相同:LeetCode - Serialize and Deserialize Binary Tree
思考:
- 二叉树的序列化需要把记录叶子节点的作用空节点也记录下来,才能实现正确的反序列化
- 观察给的测试用例,其二叉树采用的就是一个序列表示,为层序遍历,故层序遍历序列化一定可以正确地反序列化
- 由于反序列化是从根开始构建二叉树,故根应该暴露在最前面,前序/后序遍历可以进行正确的反序列化,但是中序遍历不行,因为会存在多个不同的二叉树结构具有相同的中序遍历序列,例如,考虑以下两棵不同结构的二叉树:
1 2
/ \
2 1
这两棵树具有相同的中序遍历序列(2, 1),因此仅凭中序遍历无法唯一确定二叉树的结构。
解法1:层序遍历
- 从序列重新构建二叉树需要先建立根,然后将其左右子树节点放入队列中,一层一层构建
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if(!root) return "[]";
queue<TreeNode*> q;
q.push(root);
ostringstream oss;
oss << "[";
while(!q.empty()){
if(q.front()){
oss << q.front()->val << ",";
q.push(q.front()->left);
q.push(q.front()->right);
}
else oss << "null,";
q.pop();
}
string str = oss.str();
str.pop_back();
str += "]";
return str;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data=="[]") return nullptr;
data = data.substr(1,data.size()-2);
vector<string> tokens;
string token;
istringstream iss(data);
while(getline(iss, token, ',')){
tokens.push_back(token);
}
TreeNode *root = new TreeNode(stoi(tokens[0]));
queue<TreeNode*> q;
int i=1;
q.push(root);
while(!q.empty()){
if(tokens[i]!="null"){
q.front()->left = new TreeNode(stoi(tokens[i]));
q.push(q.front()->left);
}
++i;
if(tokens[i]!="null"){
q.front()->right = new TreeNode(stoi(tokens[i]));
q.push(q.front()->right);
}
++i;
q.pop();
}
return root;
}
};
解法2:前序遍历
递归建立二叉树的核心代码:
TreeNode* node = new TreeNode(stoi(s));
node->left = deserializeHelper(q);
node->right = deserializeHelper(q);
完整代码:
class Codec {
public:
string serialize(TreeNode* root) {
if (root == nullptr) {
return "#";
}
return to_string(root->val) + "," + serialize(root->left) + "," + serialize(root->right);
}
TreeNode* deserialize(string data) {
queue<string> q;
stringstream ss(data);
string item;
while (getline(ss, item, ',')) {
q.push(item);
}
return deserializeHelper(q);
}
TreeNode* deserializeHelper(queue<string>& q) {
string s = q.front();
q.pop();
if (s == "#") {
return nullptr;
}
TreeNode* node = new TreeNode(stoi(s));
node->left = deserializeHelper(q);
node->right = deserializeHelper(q);
return node;
}
};
解法3:后序遍历
- 由于后序遍历为左右根,所以从根建立二叉树时需要从后往前建立,也就是根右左
- 从左右根取出根右左的节点顺序,可以利用栈的 LIFO 实现
class Codec {
public:
string serialize(TreeNode* root) {
if (root == nullptr) {
return "#";
}
return serialize(root->left) + "," + serialize(root->right) + "," + to_string(root->val);
}
TreeNode* deserialize(string data) {
stringstream ss(data);
string item;
stack<string> s;
while (getline(ss, item, ',')) {
s.push(item);
}
return deserializeHelper(s);
}
TreeNode* deserializeHelper(stack<string>& s) {
string val = s.top();
s.pop();
if (val == "#") {
return nullptr;
}
TreeNode* root = new TreeNode(stoi(val));
root->right = deserializeHelper(s);
root->left = deserializeHelper(s);
return root;
}
};