ACM大牛带你玩转算法与数据结构-课程资料
本笔记属于船说系列课程之一,课程链接:
你也可以选择购买『船说系列课程-年度会员』产品『船票』,畅享一年内无限制学习已上线的所有船说系列课程:船票购买入口https://www.bilibili.com/cheese/pages/packageCourseDetail?productId=598
做题网站OJ:HZOJ - Online Judge
Leetcode :力扣 (LeetCode) 全球极客挚爱的技术成长平台
二叉树:广义表表示法
二叉树转广义表:
空树用()表示
现在有一个节点A可以表示为A或A()
现在节点A又一个左节点,左节点为B,那么可以表示为,A(B,)或A(B)
如果为右节点,表示为A(,B)
如果左右都有节点表示为A(B,C)
来一个例子,如下图:
先对根节点进行表示A(,)
然后先进行对他的左子树表示A(B(,),)
然后继续左子树往下表示A(B(,D), )
然后左子树没有了,开始表示根节点的右子树A(B(,D),C(,))
继续往下进行表示A(B(,D),C(E,F))
最终结果:
A(B(,D),C(E,F))
上面就是对二叉树转换为广义表的形式的方法,下面是代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
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;
}
//插入节点
Node *insert(Node *root, int key) {
if (!root) 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;
}
void clear(Node *root) {
if (!root) return ;
clear(root->lchild);
clear(root->rchild);
free(root);
return ;
}
char buff[1000];
int len = 0;
//二叉树转广义表
//二叉树转广义表和前序遍历差不多
//所以可以按着前序遍历进行处理
void __serialize(Node *root) {
if (!root) return ;
//处理根节点,先将根节点进行表示
len += sprintf(buff + len, "%d", root->key);
//然后判断当前节点是否还有左右子树
if (!root->lchild && !root->rchild) return ;
//到了这里说明有左子树或者有右子树
//那么就需要括号,先输入进左括号
len += sprintf(buff + len, "(");
//递归左子树
__serialize(root->lchild);
//判断是否有右子树
if (root->rchild) {
//有右子树就需要 ‘,‘来隔开进行表示右子树
len += sprintf(buff + len, ",");
__serialize(root->rchild);
}
//最后说明当前左右子树递归完成,用右)进行包起来
len += sprintf(buff + len, ")");
return ;
}
void pre_out(Node *root) {
if (!root) return ;
printf("%d ", root->key);
pre_out(root->lchild);
pre_out(root->rchild);
return ;
}
void serialize(Node *root) {
memset(buff, 0, sizeof(buff));
len = 0;
__serialize(root);
return ;
}
int main() {
srand(time(0));
#define MAX_NODE 10
Node *root = getRandomBinaryTree(MAX_NODE);
serialize(root);
printf("%s\n", buff);
pre_out(root);
putchar(10);
return 0;
}
广义表转二叉树:
当前有一个广义表A(B, C)
现在可以这样来看这个广义表:
然后我们是先要处理根节点,然后在处理左子树,在处理右子树对吧。
通过图中我们先处理A,然后把A转换为一个节点,
然后就是'(',这里遇到了'('我们就应该知道他是有子树的,
然后再是B就需要处理为左子树,因为没有遇到','所以可以判断当前B为左子树
然后是','说明后面如果遇到的不是')',而这里下一个就是C,那么C就是右子树
最后遇到')'说明当前的根节点处理完成。
对于上面的处理流程,结合之前学习的数据结构,可以判断出和之前学习的栈结构一样,那么对于当前需要广义表转二叉树我们可以用栈来进行辅助处理:
- 遇到关键字,生成新节点,比如遇到A,生成节点A
- 遇到(,将刚才的新节点压入到栈中,刚刚生成的A节点压入到栈中,并设置标记为左子树
- 遇到, ,标记当前处理的是右子树
- 遇到),将栈顶节点出战
- 每生成新节点,根据标记设置左右子树
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_NODE 10
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;
}
//插入节点
Node *insert(Node *root, int key) {
if (!root) 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;
}
void clear(Node *root) {
if (!root) return ;
clear(root->lchild);
clear(root->rchild);
free(root);
return ;
}
char buff[1000];
int len = 0;
//二叉树转广义表
//二叉树转广义表和前序遍历差不多
//所以可以按着前序遍历进行处理
void __serialize(Node *root) {
if (!root) return ;
//处理根节点,先将根节点进行表示
len += sprintf(buff + len, "%d", root->key);
//然后判断当前节点是否还有左右子树
if (!root->lchild && !root->rchild) return ;
//到了这里说明有左子树或者有右子树
//那么就需要括号,先输入进左括号
len += sprintf(buff + len, "(");
//递归左子树
__serialize(root->lchild);
//判断是否有右子树
if (root->rchild) {
//有右子树就需要 ‘,‘来隔开进行表示右子树
len += sprintf(buff + len, ",");
__serialize(root->rchild);
}
//最后说明当前左右子树递归完成,用右)进行包起来
len += sprintf(buff + len, ")");
return ;
}
void pre_out(Node *root) {
if (!root) return ;
printf("%d ", root->key);
pre_out(root->lchild);
pre_out(root->rchild);
return ;
}
void serialize(Node *root) {
memset(buff, 0, sizeof(buff));
len = 0;
__serialize(root);
return ;
}
Node *deserialize(char *buff, int n) {
//创建栈进行辅助操作
Node **arr = (Node **)malloc(sizeof(Node *) * MAX_NODE);
//scode表示用来表示当前执行流程
//flag 0 表示当前处理左子树,1表示处理右子树
int top = -1, flag = 0, scode = 0;
Node *p = NULL, *root = NULL;
for (int i = 0; buff[i]; i++) {
switch(scode) {
case 0: {
//当前位置为数字时设置为状态1,处理新节点
if (buff[i] >= '0' && buff[i] <= '9') scode = 1;
//当前位置为左括号时设置为状态2,入栈当前位置的根节点,修改标记为左子树
else if (buff[i] == '(') scode = 2;
//为逗号时设置状态3,标记当前为右子树
else if (buff[i] == ',') scode = 3;
//为右括号设置状态4,弹栈
else scode = 4;
//因为0状态没有对当前字符做任何处理所以需要回溯循环
i -= 1;
} break;
case 1: {
int num = 0;
while (buff[i] <= '9' && buff[i] >= '0') {
num = num * 10 + (buff[i] - '0');
i += 1;
}
p = getNewNode(num);
if (top >= 0 && flag == 0) arr[top]->lchild = p;
if (top >= 0 && flag == 1) arr[top]->rchild = p;
i--;
scode = 0;
} break;
case 2: {
arr[++top] = p;
flag = 0;
scode = 0;
} break;
case 3: {
flag = 1;
scode = 0;
} break;
case 4: {
root = arr[top--];
scode = 0;
} break;
}
}
return p;
}
int main() {
srand(time(0));
Node *root = getRandomBinaryTree(MAX_NODE);
serialize(root);
printf("%s\n", buff);
pre_out(root);
putchar(10);
Node *new_root = deserialize(buff, len);
pre_out(root);
putchar(10);
return 0;
}