二叉树序列化

    问题:如何将二叉树序列化为一个文本文件,使得文件的大小尽可能的小。

     想了四种方法:

     第一种方法:把二叉树按前序和中序遍历一遍,存两次二叉树。

     第二种方法:将二叉树按左枝为0,右枝为1进行路径编码,那么每个节点都可以表示成,节点信息和路径信息进行永久化。 
     第三种方法:将二叉树变成满二叉树,采用数组存储满二叉树,那么数据index和根据二叉树的节点信息进行永久化。

0123456789
ROOTLRLLLRRLRRLLLLLRLRL


     第四种方法:采用一个位图和所有节点信息进行存储,位图上的0代表着相应满二叉树的节点没有节点,1代表有节点信息。
     四种方法比较:第一种方法,冗余信息量刚好是原有数据的两倍。
                                 第二种方法存储的是节点信息和路径信息,冗余信息量是(路径信息*节点数)
                                 第三种方法:冗余信息为,sizeof(unsigned int) * 节点数
                                 第四种方法:冗余信息为树的层数K,(2^k -1)bit 
      因此第四种方法在二叉树是满二叉树或者二叉树的层数比较小的情况下,信息量比较小,但是在树的层比较丰富的情况下,冗余信息非常大。我觉得也不是什么特别好的方法。貌似我还是没有得到正确答案。

#include<iostream>
#include <fstream>
#include<math.h>

#define  max(a,b) ((a)>(b)?(a):(b))

using namespace std;
//随机数大小
const int NUMBER = 9;
//修改树的深度
const int DEPTH = 6;

//文件流
ofstream fout3("serialize3.txt");
ofstream fout4("serialize4.txt");
ofstream fout("tree.txt");

//树节点信息
typedef struct Node
{
	int data;
	Node * left;
	Node * right;
} BinaryTree;


//随机生成二叉树
void generate(Node ** tree, int d)
{
	*tree = (Node *)malloc(sizeof(Node));
	(*tree)->data = rand()%NUMBER + 1;

	int isleft = rand()%DEPTH;
	if(d+isleft < DEPTH)
		generate(&((*tree)->left), d+1);
	else
		(*tree)->left = NULL;
	
	int isright = rand()%DEPTH;
	if (d+isright < DEPTH)
	{
		generate(&((*tree)->right), d+1);
	}
	else
		(*tree)->right = NULL;
}

//获取树的深度
int getTreeDepth(Node * tree)
{
	if (tree == NULL)
	{
		return 0;
	}
	int left = getTreeDepth(tree->left);
	int right = getTreeDepth(tree->right);
	
	return (max( left, right ) + 1);
}

//打印第i层树
void printTreeLevel(Node * tree, int index, int level,int depth)
{
	if(tree == NULL)
	{
		int length = pow(2.0,(depth-index))-1;
		for (int i=1; i <= length; i ++)
		{
			fout << "  ";
			cout <<" ";
		}
		return;
	}
	if(index == level)
	{
		//左子树宽度
		int length = pow(2.0,(depth-level-1))-1;
		for (int j=1; j <= length; j ++)
		{
			fout <<"  ";
			cout <<" ";
		}
		fout << tree->data;
		cout << tree->data;
		//右子树宽度
		for (int j=1; j <= length; j ++)
		{
			fout <<"  ";
			cout <<" ";
		}
		return;
	}

	printTreeLevel(tree->left, index +1,level, depth);
	fout <<"  ";
	cout <<" ";
	printTreeLevel(tree->right, index+1, level, depth);
}

//逐层遍历二叉树
//两种思路,一种采用广度遍历的方法,利用链表空间逐层存储,逐层打印
//一种使用递归的方法逐层打印
void printTree(Node * tree)
{
	int depth = getTreeDepth(tree);

	for (int i=0; i < depth; i ++)
	{
		printTreeLevel(tree, 0, i,depth);
		fout << endl;
		cout <<endl;
	}
}

//序列化,四种方法
//第一种方法:把二叉树按前序和中序遍历一遍,存两次二叉树。
//第二种方法:将二叉树按左枝为0,右枝为1进行路径编码,那么每个节点都可以表示成,节点信息和路径信息进行永久化。
//第三种方法:将二叉树变成满二叉树,采用数组存储满二叉树,那么数据index和根据二叉树的节点信息进行永久化。
//第四种方法:采用一个位图和所有节点信息进行存储,位图上的0代表着相应满二叉树的节点没有节点,1代表有节点信息。
//四种方法比较:第一种方法,冗余信息量刚好是原有数据的两倍。
//第二种方法存储的是节点信息和路径信息,冗余信息量是(路径信息*节点数)
//第三种方法:冗余信息为,sizeof(unsigned int) * 节点数
//第四种方法:冗余信息为树的层数K,(2^k -1)bit 
//因此第四种方法在二叉树是满二叉树或者二叉树的层数比较小的情况下,信息量比较小,但是在树的层比较丰富的情况下,
//冗余信息非常大。我觉得也不是什么特别好的方法。貌似我还是没有得到正确答案。

//第三种方法序列化
void serialize_3(Node *tree, unsigned int index)
{
	if (tree == NULL)
	{
		return;
	}
	
	fout3 << tree->data << " " << index << endl;
	serialize_3(tree->left, index*2);
	serialize_3(tree->right, index*2 +1);
}

//设置bitmap
void setbitmap(Node * tree, int * map,unsigned int bit, int level)
{
	if (tree == NULL)
	{
		return;
	}
	
	unsigned int index = bit / 32;
	unsigned int b = bit%32;
	map[index] = map[index] | (1<<b);
	fout4 << tree->data << " ";
	setbitmap(tree->left, map,  bit * 2+1, level+1);
	setbitmap(tree->right, map, bit * 2 +2 , level+1);
}

//第四种方法永久化
void seralize_4(Node* tree)
{
	int depth = getTreeDepth(tree);
	
	int len = (depth-5)>0? (depth-5) : 0;
	len = (1<<len);
	int* map = new int[len];
	memset(map, 0, sizeof(int) * len);
	setbitmap(tree, map, 0, 1);

	fout4<<endl;

	for (int i=0; i <len; i ++)
	{
		fout4 <<hex << map[i];
	}
	fout4 <<endl;
	delete [] map;
}

//释放内存
void deleteTree(Node * tree)
{
	if (tree == NULL)
	{
		return;
	}
	deleteTree(tree->left);
	deleteTree(tree->right);
	free(tree);
}

int main()
{
	BinaryTree * tree=NULL;

	//随机生成二叉树
	cout << "随机生成二叉树,并保存到tree.txt"<<endl;
	generate(&tree, 0);

	//树层比较低的,打印出二叉树
	if (DEPTH <= 15)
	{
		printTree(tree);
	}

	//第三种序列化方法
	cout << "用第三种方法持久化到serialize3.txt" <<endl;
	serialize_3(tree, 1);

	//第四种序列化方法
	cout <<"用第四种方法持久化到serialize4.txt" <<endl;
	seralize_4(tree);

	//回收空间
	deleteTree(tree);
	system("pause");
	return 0;
}


    运行效果:

    第一步,随机生成一个二叉树,并逐层打印,然后采用两种序列化方法进行序列化。


     第三种序列化文件为:

6 1
8 2
9 4
8 5
3 11
7 22
9 23
7 3
1 6
8 12
6 24
3 49
9 7
4 15
3 31
    第四种序列化方法为:

6 8 9 8 3 7 9 7 1 8 6 3 9 4 3       --数据
40e04c7f10000                       --位图,用16进制表示
   如上面分析的一样,第四种方法在二叉树是满二叉树或者二叉树的层数比较小的情况下,信息量比较小,但是在树的层比较丰富的情况下,冗余信息非常大。如在15层的二叉树中:
6 8 9 4 8 3 7 6 4 1 1 6 3 7 9 8 3 7 3 6 3 8 3 7 2 5 6 4 3 1 8 4 2 2 4 3 4 1 9 6 7 4 7 9 5 8 8 7 3 7 9 3 6 3 9 3 3 2 3 5 1 7 5 1 3 9 2 1 3 1 3 3 6 6 2 7 9 4 6 2 7 9 3 2 1 7 4 3 3 1 9 3 1 9 7 6 1 8 8 7 3 2 6 6 8 5 2 3 8 2 5 5 9 4 6 9 2 3 1 2 2 5 2 1 9 9 4 7 6 4 8 5 9 4 8 9 9 9 6 9 5 8 3 9 9 8 3 9 3 5 6 9 6 2 2 6 2 2 5 1 2 1 1 5 6 9 9 5 8 9 5 4 3 8 6 2 3 4 9 9 2 7 7 8 7 6 7 2 2 9 8 1 3 1 3 3 4 3 3 7 3 5 7 9 7 2 4 3 5 2 1 5 3 9 2 8 3 6 3 2 2 1 2 9 3 1 5 5 8 2 4 6 3 4 2 5 5 5 7 6 4 6 5 8 6 2 5 9 6 3 6 3 1 8 1 9 4 6 9 2 4 1 1 5 3 7 2 2 7 1 8 3 4 9 2 6 9 3 8 
3e8ffbff1f7901eb3d9867fe6004a7860038b380000606daf48130098068070189048001e8000009022418079e361e000007000838000794808400040800000001f1800018000820008006605401300580650004600000002e82801d8000802120000000018000000002c000118000008000000060000404105800000000400000100000000001928001000800001a21100000000000000008000000020000000000860000000020030000004000000020042000000000000000000000000000000200400098000005000000020000000000000000000000000000000000080000000000000000000000400000000000000000000002000000020180000100000000000000000000000000000000000000000000000000000000000000020000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000002000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000

    信息量随着层数的增加,信息量成几何级扩大。但是,研究16进制字符串位图数据可以发现里面的数据0出现的特别多,因此下一步可以采用huffman编码等压缩编码方式对字符串数据进行数据压缩。

    下一步:采用二进制编码对位图数据进行数据压缩。









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉树序列化是指将二叉树转换为一个字符串,以便存储或传输。二叉树的反序列化是指将序列化后的字符串转换为原始二叉树。 对于二叉树序列化,可以采用前序遍历或层序遍历的方式。以下是使用前序遍历实现二叉树序列化: 1. 将根节点的值转换为字符串,并将其添加到序列化字符串中。 2. 递归地对左子树进行序列化,将其返回的字符串添加到序列化字符串中。 3. 递归地对右子树进行序列化,将其返回的字符串添加到序列化字符串中。 以下是使用前序遍历实现二叉树的反序列化: 1. 将序列化字符串按照逗号分隔符进行拆分,得到一个字符串数组。 2. 从字符串数组中取出第一个元素作为根节点的值,并将其从数组中删除。 3. 创建一个新的节点,并将其值设置为根节点的值。 4. 递归地对左子树进行反序列化,将其返回的节点设置为新节点的左子节点。 5. 递归地对右子树进行反序列化,将其返回的节点设置为新节点的右子节点。 6. 返回新节点。 以下是使用 Python 实现二叉树序列化和反序列化: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right class Codec: def serialize(self, root: TreeNode) -> str: if not root: return "" res = str(root.val) left = self.serialize(root.left) right = self.serialize(root.right) if left: res += "," + left if right: res += "," + right return res def deserialize(self, data: str) -> TreeNode: if not data: return None nodes = data.split(",") return self.buildTree(nodes) def buildTree(self, nodes): if not nodes: return None val = int(nodes.pop(0)) root = TreeNode(val) root.left = self.buildTree(nodes) root.right = self.buildTree(nodes) return root ``` 使用前序遍历序列化二叉树,例如: ```python root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.right.left = TreeNode(4) root.right.right = TreeNode(5) codec = Codec() serialized = codec.serialize(root) print(serialized) # "1,2,,3,,4,,5," ``` 使用前序遍历反序列化二叉树,例如: ```python deserialized = codec.deserialize(serialized) print(deserialized.val) # 1 print(deserialized.left.val) # 2 print(deserialized.right.val) # 3 print(deserialized.right.left.val) # 4 print(deserialized.right.right.val) # 5 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值