0%

序列化二叉树

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,我们可以把一个只有根节点为1的二叉树序列化为”1,”,然后通过自己的函数来解析回这个二叉树

解答

树中节点值不重复的情况下可使用如下方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* 保存前序和中序序列
*/
String Serialize(TreeNode root) {
StringBuilder pre = new StringBuilder();
getPreOrder(root, pre);
StringBuilder in = new StringBuilder();
getInOrder(root, in);
return pre + ";" + in;
}

private void getInOrder(TreeNode root, StringBuilder builder) {
if (root == null) {
return;
}
getInOrder(root.left, builder);
builder.append(root.val + ",");
getInOrder(root.right, builder);
}

private void getPreOrder(TreeNode root, StringBuilder builder) {
if (root == null) {
return;
}
builder.append(root.val + ",");
getPreOrder(root.left, builder);
getPreOrder(root.right, builder);
}

/**
* 递归建树
*/
TreeNode Deserialize(String str) {
String[] strs = str.split(";");
if (strs.length == 0) {
return null;
}
String[] pre = strs[0].split(",");
String[] in = strs[1].split(",");

TreeNode root = buildTree(pre, in);
return root;
}

private TreeNode buildTree(String[] pre, String[] in) {
if (pre == null || pre.length == 0) {
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(pre[0]));
int leftNum = 0;
for (int i = 0; i < in.length; i++) {
if (in[i].equals(pre[0])) {
leftNum = i;
break;
}
}

root.left = buildTree(Arrays.copyOfRange(pre, 1, leftNum + 1), Arrays.copyOfRange(in, 0, leftNum));
root.right = buildTree(Arrays.copyOfRange(pre, leftNum + 1, pre.length), Arrays.copyOfRange(in, leftNum + 1, in.length));

return root;
}

官方题解

链接:https://www.nowcoder.com/questionTerminal/cf7e25aa97c04cc1a68c8f040e71fb84?answerType=1&f=discussion
来源:牛客网

方法一:先序遍历实现

预备知识:先序遍历的递归实现:

1
2
3
4
5
6
7
8
9
10
void pre_order(TreeNode *root) {
if (!root) {
return;
}

// process root
// ...
pre_order(root->left);
pre_order(root->right);
}

对于本题来说,可以套用上述模板。
假设序列化的结果为字符串 str, 初始str = “”.根据要求,遇到nullptr节点,str += “#”
遇到非空节点,str += “val” + “!”; 假设val为3, 就是 str += “3!”

所以,最终的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char* Serialize(TreeNode *root) {    
if (!root) {
return "#";
}

string res = to_string(root->val);
res.push_back(',');

char* left = Serialize(root->left);
char* right = Serialize(root->right);

char* ret = new char[strlen(left)+strlen(right)+res.size()];
// 如果是string类型,直接用operator += ,这里char* 需要用函数
strcpy(ret,res.c_str());
strcat(ret,left);
strcat(ret,right);

return ret;
}

反序列化的结果,就是根据先序遍历,再重建二叉树即可。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 参数使用引用&, 以实现全局变量的目的
TreeNode* deseri(char *&s) {
if (*s == '#') {
++s;
return nullptr;
}

// 构造根节点值
int num = 0;
while (*s != ',') {
num = num * 10 + (*s - '0');
++s;
}
++s;
// 递归构造树
TreeNode *root = new TreeNode(num);
root->left = deseri(s);
root->right = deseri(s);

return root;
}

TreeNode* Deserialize(char *str) {
return deseri(str);
}

中序遍历,后序遍历大致都差不多。

方法二:层次遍历实现

层次遍历采用队列实现。跟先序遍历的思想差不多,无非都是把树的所有数据遍历一遍,然后记录下来。
层次遍历模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void bfs(TreeNode *root){
queue<TreeNode*> qt;
qt.push(root);
string s;
while (!qt.empty())
{
// pop operator
TreeNode *node = qt.front();
qt.pop();

// process node
if (node == NULL)
{
s.push_back('#');
s.push_back(',');
continue;
}
s += to_string(node->val);
s.push_back(',');

// push operator
qt.push(node->left);
qt.push(node->right);

}
}

序列化的操作直接根据模板套即可。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
char* Serialize(TreeNode *root)
{
string s;
queue<TreeNode*> qt;
qt.push(root);

while (!qt.empty())
{
// pop operator
TreeNode *node = qt.front();
qt.pop();
// process null node
if (node == nullptr)
{
s.push_back('N');
s.push_back(',');
continue;
}
// process not null node
s += to_string(node->val);
s.push_back(',');
// push operator
qt.push(node->left);
qt.push(node->right);

}

char *ret = new char[s.length() + 1];
strcpy(ret, s.c_str());

return ret;
}

反序列化就是根据层次遍历在走一遍即可。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
TreeNode* Deserialize(char *str)
{
if (str == nullptr) {
return nullptr;
}
// 可用string成员函数
string s(str);
if (str[0] == '#') {
return nullptr;
}

// 构造头结点
queue<TreeNode*> nodes;
TreeNode *ret = new TreeNode(atoi(s.c_str()));
s = s.substr(s.find_first_of(',') + 1);
nodes.push(ret);
// 根据序列化字符串再层次遍历一遍,来构造树
while (!nodes.empty() && !s.empty())
{
TreeNode *node = nodes.front();
nodes.pop();
if (s[0] == '#')
{
node->left = nullptr;
s = s.substr(2);
}
else
{
node->left = new TreeNode(atoi(s.c_str()));
nodes.push(node->left);
s = s.substr(s.find_first_of(',') + 1);
}

if (s[0] == '#')
{
node->right = nullptr;
s = s.substr(2);
}
else
{
node->right = new TreeNode(atoi(s.c_str()));
nodes.push(node->right);
s = s.substr(s.find_first_of(',') + 1);
}
}
return ret;
}

此题主要考察对树的遍历和构造树。