过程其实很简单:
- 先在先序子序列中找到当前子树的根节点,即先序子序列的第一个节点就是当前子树根节点
- 在中序子序列中找到当前根节点的位置,并返回下标
- 根据中序子序列中的当前子树根节点的位置,得到子树的左子树和右子树
- 根据当前子树的左右子树,分别得到其在先序子序列和中序子序列中的开始索引和结束索引
- 根据得到的索引,判断左右子树是否为空,如果不为空则返回第一步继续执行,如果为空直接返回。
在二叉链表构建完成后,通过广度优先便利来验证构建的二叉树是否正确。
// p126-6_根据先序中序序列建立二叉链表.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
根据先序序列和中序序列构建二叉树链表, 再通过广度优先遍历检验构建的二叉链表是否正确.
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
# define MAXSIZE 1000 //定义循环队列的最大范围
// 定义二叉树节点的数据结构
typedef struct LNode {
char ch;
struct LNode *lchild, *rchild = NULL;
} LNode, *LinkList;
// 在中序序列中找到当前子树根节点的位置,返回下标
int find_root_index(char* in_order, char root, int in_start_index, int in_end_index) {
for (int i = in_start_index; i <= in_end_index; ++i) {
if (in_order[i] == root) {
return i;
}
}
}
LNode* construct_binary_tree(char* pre_order, char* in_order, int pre_start_index, int pre_end_index, int in_start_index, int in_end_index) {
/*
循环其实主要考虑三块内容:
循环结束条件
循环体
循环返回值
*/
// 循环结束条件
if (pre_start_index > pre_end_index) {
return NULL;
}
//循环体
int root = pre_order[pre_start_index]; // 先序序列的第一个元素为当前子树的根节点
int root_index = find_root_index(in_order, root, in_start_index, in_end_index); // 在中序序列中找到根节点的下标
// 找到先序序列中左子树的最后一个节点
int left_end = pre_start_index + (root_index - in_start_index);
printf("当前根节点为----%c\n", root);
// 根据中序序列中根节点的位置划分出当前根节点的两棵子树
LinkList p = (LinkList)malloc(sizeof(LNode));
p->ch = root;
p -> lchild = construct_binary_tree(pre_order, in_order, pre_start_index+1, left_end, in_start_index, root_index-1);
p->rchild = construct_binary_tree(pre_order, in_order, left_end+1, pre_end_index, root_index+1, in_end_index);
// 返回值
return p;
}
// 广度搜索遍历当前构建的二叉树
void traversal_binary_tree(LNode* root) {
LNode* queue[1000]; // 循环队列,用来实现广搜
int front = 0, rear = 0; // 队头、队尾指针,队头指针指向当前队头元素,队尾指针指向当前队尾的下一个位置
int depth = 1; // 当前的层数
// 初始化循环队列
queue[rear] = root;
rear = (rear + 1) % MAXSIZE;
// 遍历循环队列,直到队列为空结束。输出每一层中的兄弟节点,即在一层中同一父节点的孩子节点一起输出,不同父节点的孩子节点之间用空格分隔
/*
判断队空队满的方法:牺牲一个单元来区分队空队满
初始时:front = rear = 0
队首元素出队列:front = (front+1) % MAXSIZE
队尾新加元素入队列:rear = (rear+1) % MAXSIZE
队列长度:(rear+MAXSIZE-front) % MAXSIZE
判断队满:(rear+1) % MAXSIZE = front
判断队空:front = rear
*/
// 广搜需要确保一次遍历后当前层的节点全部出队,入队的都为下一层节点,即当前层节点的孩子节点
while (front != rear) {
// 每一次循环入队下一层节点,出队当前层节点
int cur_pos = rear; // 当前队列中的最后一个节点的下一个位置,这个位置之前的所有节点都需要出队列
for (; front != cur_pos; front = (front+1)%MAXSIZE) { // 这里一定要注意出队入队时front和rear值的变化
printf("%c", queue[front]->ch);
if (queue[front]->lchild != NULL) { // 如果有左孩子节点,则入队
queue[rear] = queue[front]->lchild;
rear = (rear + 1) % MAXSIZE;
//printf("lchild--%c", queue[front]->lchild);
}
if (queue[front]->rchild != NULL) { // 如果有右孩子节点,则入队
queue[rear] = queue[front]->rchild;
rear = (rear + 1) % MAXSIZE;
//printf("rchild--%c\n", queue[front]->rchild);
}
}
printf("\n"); // 结束每一层的输出后换行
}
}
int main()
{
/*
测试用例:
先序序列:ABDECFHIG
中序序列:DBEAHFICG
对二叉树的广搜结果:
A
B C
DE FG
HI
*/
char pre_order[1000], in_order[1000];
scanf("%s", pre_order);
scanf("%s", in_order);
printf("%s--%s\n", pre_order, in_order);
LinkList root = construct_binary_tree(pre_order, in_order, 0, strlen(pre_order)-1, 0, strlen(pre_order)-1);
traversal_binary_tree(root);
return 0;
}