后序遍历满二叉树(洛谷P1087 FBI树)
题目
见题目链接FBI树 - 洛谷
题目链接
输入
见题目链接
输出
见题目链接
样例
见题目链接
题解
本题可划分为两部分的问题来进行解决,首先是利用给定的01串来构造一个满二叉树,其中要对01串进行类似于二分的操作,因此我们可以使用递归的思路,将01串不断二分,并得到相应节点对应的字母(F、B、I),由此构成一颗满二叉树。我们采用一个带有两个指针域的结构体链表来存储这颗树,两个指针分别指向该节点的左儿子和右儿子。这样构造的好处是在进行下一步后序遍历时,可以方便地找到当前节点的两个儿子。构造完满二叉树后,就可以开始对其进行后序遍历了。根据“左右根”的思路,我们可以用类似DFS(深度优先搜索)的方式来进行后序遍历,每次寻找到最深的那个节点,遍历结束后回溯到上一层,重复这两个操作,直到遍历完全部的节点。具体的代码实现还是运用递归的思想。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct tree{
char flag;
struct tree *next_1, *next_2;
}LinkList;
LinkList *head;
int makeString(char* str, LinkList *node, int len){
LinkList *node1, *node2;
int flag0 = 0;
int flag1 = 0;
node1 = (LinkList*)malloc(sizeof(LinkList));
node2 = (LinkList*)malloc(sizeof(LinkList));
if (len > 1){
if (head != node){
node->next_1 = node1;
node->next_2 = node2;
for (int i = 0; i < len/2;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node1->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node1->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node1->flag = 'I';
flag0 = 0;
flag1 = 0;
for (int i = len/2; i < len;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node2->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node2->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node2->flag = 'I';
makeString(str, node1, len/2);
makeString(str+len/2, node2, len/2);
}else{
node->next_1 = node1;
for (int I = 0; I < len;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node1->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node1->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node1->flag = 'I';
makeString(str, node1, len);
}
}else{
if (head != node){
node->next_1 = NULL;
node->next_2 = NULL;
}
else{
node->next_1 = node1;
if (str[0] == '0')
node1->flag = 'B';
if (str[0] == '1')
node1->flag = 'I';
node1->next_1 = NULL;
node2->next_2 = NULL;
}
}
return 0;
}
int search(LinkList *node){
if ((node->next_1 == NULL) && (node->next_2 == NULL)){
printf("%c", node->flag);
}else{
search(node->next_1);
search(node->next_2);
printf("%c", node->flag);
}
return 0;
}
int main(){
char str[1025];
int n,sum;
head = (LinkList*)malloc(sizeof(LinkList));
scanf("%d", &n);
sum = 1;
for (int i = 0; i < n;i++){
sum = sum * 2;
}
sum = sum * 2 - 1;
scanf("%s", str);
makeString(str, head, (sum+1)/2);
search(head->next_1);
return 0;
}
代码解释
链表结构体声明:
typedef struct tree{
char flag;
struct tree *next_1, *next_2; //next_1指向左儿子,next_2指向右儿子
}LinkList;
构造满二叉树:
int makeString(char* str, LinkList *node, int len){ // str是字符串指针 node是指向当前节点的链表指针 len是当前节点对应的字符串的长度
LinkList *node1, *node2;
int flag0 = 0;
int flag1 = 0;
node1 = (LinkList*)malloc(sizeof(LinkList));
node2 = (LinkList*)malloc(sizeof(LinkList));
if (len > 1){
if (head != node){ //如果当前节点不是头节点,则构造两个儿子节点,并将当前节点指向这两个儿子节点
node->next_1 = node1;
node->next_2 = node2;
//求得节点的char值
for (int i = 0; i < len/2;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node1->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node1->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node1->flag = 'I';
flag0 = 0;
flag1 = 0;
for (int i = len/2; i < len;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node2->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node2->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node2->flag = 'I';
//对两个儿子节点重复构造的过程(递归)
makeString(str, node1, len/2);
makeString(str+len/2, node2, len/2);
}else{
//如果当前节点是头节点,则创建一个根节点,并将头节点指向这个根节点
node->next_1 = node1;
for (int I = 0; I < len;i++){
if (str[i] == '0')
flag0 = 1;
if (str[i] == '1')
flag1 = 1;
}
if (flag0 == 1 && flag1 == 1)
node1->flag = 'F';
if (flag0 == 1 && flag1 == 0)
node1->flag = 'B';
if (flag0 == 0 && flag1 == 1)
node1->flag = 'I';
makeString(str, node1, len);
}
}else{
//如果01串的长度为1,有两种情况,一个是经过多次二分,01串分解成了只剩一个char的串,无法继续再二分,因此当前节点不存在儿子,将两个指向儿子的指针设为NULL;另一种情况是,输入的01串总共只有一个char,此时要建立一个根节点,并将头节点的指针指向它,将这个根节点的两个指向儿子的指针设为NULL。
if (head != node){
node->next_1 = NULL;
node->next_2 = NULL;
}
else{
node->next_1 = node1;
if (str[0] == '0')
node1->flag = 'B';
if (str[0] == '1')
node1->flag = 'I';
node1->next_1 = NULL;
node2->next_2 = NULL;
}
}
//退出当前函数调用(回溯)
return 0;
}
进行后序遍历:
int search(LinkList *node){
if ((node->next_1 == NULL) && (node->next_2 == NULL)){
printf("%c", node->flag); //如果找到了叶子节点,说明已经到达了树的最深处,此时应输出当前节点的char值,并回溯到上一个节点。
}else{
//如果没有找到叶子节点,则继续按照“左右根”的顺序查找下一个节点
search(node->next_1);
search(node->next_2);
printf("%c", node->flag); //当儿子节点都遍历完了之后,回溯到此节点,输出此节点的char值
}
return 0;//退出当前函数调用(回溯)
}