二叉树的反序列化(广义表转二叉树)

当我们需要在另一台计算机中重构一棵二叉树,我们便需要对序列化的二叉树反序列化。

举个栗子!

我们有这样的二叉树序列:A(B(D),C(E,F))

我们人类用肉眼便可以一下子看出这棵二叉树的结构

如图所示,很简单吧!

可是计算机处理广义表没有人脑那么智能o(╥﹏╥)o,我们必须另寻他法!

看到广义表的结构(完全包含关系),便可以想到一个基本数据结构:栈!

要解决这个比较复杂的问题,我们由浅处着手。

1.假如这棵树的广义表序列只有一个结点A,我们如何去做?

当然这很简单,我们只需要创建一个结点A,并将它作为root便可以了。

2.加一点点难度呢?我们需要转换一个广义表序列为A( , B)的树呢?

现在想象你自己是一台计算机!你只会从左向右慢慢扫描字符!

①当我们扫描到A字符,我们可以先创建一个A节点。

②当我们扫描到'(',我们知道A节点存在子树,我们想要构建A的子树节点,所以我们可以先将A压入栈中。

③当我们扫描到‘,’,我们知道应当处理的下个节点是A节点的右子树,我们设置一个标记(用一个flag变量设置)。

④当我们扫描到B字符,我们创建B节点,并且设置栈顶元素A节点的右子树为B。

⑤当我们扫描到‘)’,标志着我们已经处理完成了栈顶节点A的左右子树,所以我们将A弹栈,完成本次反序列化操作。

 由这样一个简单的例子,我们可以抽象出处理的法则!

一、遇到关键字:生成新节点,并且处理与上一个节点的关系。

二、遇到左括号:最新节点入栈。

三、遇到逗号:设置下一个处理的节点为栈顶节点的右子树(作标记)。

四、遇到右括号:弹栈。

大概就是这样的一个方法了,下面给出一种实现代码,代码使用了状态机的原理,仅供参考!

Node *deserialize(char *buff, int n) {
	Node **s = new Node*[MAX_NODE];
	//用数组模拟一个栈,指向节点的指针数组
	int top = -1, flag = 0, scode = 0;
	//flag为标记数:0为左子树,1为右子树
	//scode为状态码
	Node *p = NULL;//p永远指向最后一个生成的节点
	Node *root = NULL;
	for(int i = 0; buff[i] ; i++){//状态机原理
		switch(scode){
			//0状态码:根据当前字符任务分发:
			/*关键字:跳转到1状态码
			  左括号:跳转到2状态码
			  逗号  :跳转到3状态码
			  右括号:跳转到4状态码
			 */
		case 0:{
			if(buff[i]>= '0'&&buff[i] <= '9') scode = 1;
			else if(buff[i] == '(') scode = 2;
			else if(buff[i] == ',') scode = 3;
			else scode = 4;
			i -= 1;
		}break;
		case 1:{//生成一个新节点
			int num = 0;
			while(buff[i]>= '0'&&buff[i] <= '9'){
				num = num * 10 + (buff[i] - '0');
				i++;
			}
			p = getNewNode(num);
			if(top>=0 && flag == 0) s[top]->lchild = p;
			if(top>=0 && flag == 1) s[top]->rchild = p;
			i--;
			scode = 0;
		}break;
		case 2:{//节点入栈
			s[++top] = p;
			flag = 0;
			scode = 0;
		}break;
		case 3:{//设置下一个处理的是右子树
			flag = 1;
			scode = 0;
		}break;
		case 4:{//弹栈
			root = s[top];
			top -= 1;
			scode = 0;
		}break;
		}
	}
	return root;
}

完整测试代码:

#include <iostream>
#include <cstdio>
#include <time.h>
#include <string.h>
#define MAX_NODE 10
#define KEY(n) (n? n->key:-1)
using namespace std;
typedef struct Node {
	int key;
	Node *lchild, *rchild;
} Node;

Node *getNewNode(int key) {
	Node *p = new Node;
	p->key = key;
	p->lchild = p->rchild = NULL;
	return p;
}

void clear(Node *root) {
	if (root == NULL) return;
	clear(root->lchild);
	clear(root->rchild);
	delete root;
}
Node *insert(Node *root, int key) {
	if (root == NULL) return getNewNode(key);
	if (rand() % 2) root->lchild = insert(root->lchild, key);
	else root->rchild = insert(root->rchild, key);
	return root;
}

Node *getRandomBinaryTree(int n) {
	Node *root = NULL;
	for (int i = 0; i < n; i++) {
		root = insert(root, rand() % 100);
	}
	return root;
}
char buff[1000];
int len = 0;

void __serialize(Node *root) {
	if (root == NULL) return;
	len += snprintf(buff + len, 100, "%d", root->key); //向字符数组中输出信息
	if (root->lchild == NULL && root->rchild == NULL) return;
	len += snprintf(buff + len, 100, "(");
	__serialize(root->lchild);
	if (root->rchild) {
		len += snprintf(buff + len, 100, ",");
		__serialize(root->rchild);
	}
	len += snprintf(buff + len, 100, ")");
	return;
}

void serialize(Node *root) {
	memset(buff, 0, sizeof(buff)); //初始化buff
	len = 0;
	__serialize(root);
	return;
}

void print(Node *node) {
	printf("%d(%d,%d)\n", KEY(node),
	       KEY(node->lchild),
	       KEY(node->rchild));
	return;
}

void output(Node *root) {
	if (root == NULL) return;
	print(root);
	output(root->lchild);
	output(root->rchild);
	return;
}

Node *deserialize(char *buff, int n) {
	Node **s = new Node*[MAX_NODE];
	//用数组模拟一个栈,指向节点的指针数组
	int top = -1, flag = 0, scode = 0;
	//flag为标记数:0为左子树,1为右子树
	//scode为状态码
	Node *p = NULL;//p永远指向最后一个生成的节点
	Node *root = NULL;
	for(int i = 0; buff[i] ; i++){//状态机原理
		switch(scode){
			//0状态码:根据当前字符任务分发:
			/*关键字:跳转到1状态码
			  左括号:跳转到2状态码
			  逗号  :跳转到3状态码
			  右括号:跳转到4状态码
			 */
		case 0:{
			if(buff[i]>= '0'&&buff[i] <= '9') scode = 1;
			else if(buff[i] == '(') scode = 2;
			else if(buff[i] == ',') scode = 3;
			else scode = 4;
			i -= 1;
		}break;
		case 1:{//生成一个新节点
			int num = 0;
			while(buff[i]>= '0'&&buff[i] <= '9'){
				num = num * 10 + (buff[i] - '0');
				i++;
			}
			p = getNewNode(num);
			if(top>=0 && flag == 0) s[top]->lchild = p;
			if(top>=0 && flag == 1) s[top]->rchild = p;
			i--;
			scode = 0;
		}break;
		case 2:{//节点入栈
			s[++top] = p;
			flag = 0;
			scode = 0;
		}break;
		case 3:{//设置下一个处理的是右子树
			flag = 1;
			scode = 0;
		}break;
		case 4:{//弹栈
			root = s[top];
			top -= 1;
			scode = 0;
		}break;
		}
	}
	return root;
}

int main() {
	srand(time(0));
	Node *root = getRandomBinaryTree(MAX_NODE);
	serialize(root);//调用序列化
	output(root);
	printf("Buff[]:%s\n", buff);
	Node *new_root = deserialize(buff, len); //反序列化
	output(new_root);
	return 0;
}

 

感谢观看!*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值