树和二叉树_3
一、二叉树与广义表
为了更好的将二叉树序列化,便于存储和传输。我们引入二叉树的广义表示法。
- 空树 ()
- 只有一个节点A A/A()
- A有一个左孩子B A(B,)/A(B)
- A有一个左孩子B和一个右孩子C A(B,C)
二、二叉树的广义表示法
- 如何将二叉树转换为广义表
- 如何将广义表转换为二叉树
三、代码实现
//二叉树的广义表示法
//序列化和反序列化,以下是序列化
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#define KEY(n) (n?n->key:-1)
typedef struct Node{
int key;
struct Node *lchild,*rchild;
}Node;
Node *getNewNode(int key){
Node *p=(Node *)malloc(sizeof(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);
free(root);
return ;
}
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;
}
//随机生成n个节点二叉树
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+=sprintf(buff+len,"%d",root->key);//sprintf函数会返回输入了几个字符
if(root->lchild==NULL&&root->rchild==NULL) return ;
len+=sprintf(buff+len,"(");
__serialize(root->lchild);
if(root->rchild){
len+=sprintf(buff+len,",");
__serialize(root->rchild);
}
len+=sprintf(buff+len,")");
return ;
}
void serialize(Node *root){
memset(buff,0,sizeof(buff));
len=0;
__serialize(root);
return ;
}
void print(Node *root){
printf("%d(%d,%d)\n",KEY(root),KEY(root->lchild),KEY(root->rchild)
);
return ;
}
void output(Node *root){
if(root==NULL) return ;
print(root);
output(root->lchild);
output(root->rchild);
return ;
}
int main(){
srand(time(0));
#define MAX_NODE 10
Node *root=getRandomBinaryTree(MAX_NODE);
output(root);
serialize(root);
printf("buff[]:%s\n",buff);
return 0;
}
//反序列化
Node *deserialize(char *buff,int n){
Node **s=(Node **)malloc(sizeof(Node *)* MAX_NODE);//新建一个数组去模拟栈
int top=-1,flag=0,scode=0;//参考状态机,设置一个状态码scode,初始0任务分发,关键字1左括号2逗号3右括号4
Node *p=NULL,*root=NULL;
for(int i=0;buff[i];i++){
switch(scode){
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;//0没处理任何字符,恢复i
} break;
case 1:{
int key=0;
while(buff[i]>='0'&&buff[i]<='9'){
key=key*10+(buff[i]-'0');
i+=1;
}
p=getNewNode(key);
//printf("p->key:%d\n",p->key);
//printf("top:%d\n",top);
if(top>=0&&flag==0){
s[top]->lchild=p;
//printf("%d->lchild:%d\n",s[top]->key,p->key);
}
if(top>=0&&flag==1){
s[top]->rchild=p;
//printf("%d->rchild:%d\n",s[top]->key,p->key);
}
i-=1;//将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--];
scode=0;
}break;
}
}
return root;
}
int main(){
srand(time(0));
Node *root=getRandomBinaryTree(MAX_NODE);
output(root);
serialize(root);
printf("buff[]:%s\n",buff);
Node *new_root=deserialize(buff,len);
printf("new_root:\n");
output(new_root);
return 0;
}
思考:使用状态机理念来进行状态转换,可以让case0不用break,直接继续往下走其他case,这样也可以省去i–;而case1中的i–不能去掉,去掉之后下标进入’(‘而外层循环会+1,导致跳过’('。