(此题在原题PAT A1020的基础上进行了适当的改编,原题请点击此链接:PAT A1020. Tree Traversals)
问题
给出一棵二叉树的先序遍历序列和中序遍历序列,求这颗二叉树的层序遍历序列(假设这棵二叉树的每一个结点数值均不相同)。
思路
如果要用先序遍历的方法来遍历一棵二叉树,那么总是先访问根结点,再访问左子树和右子树,因此根结点一定位于先序遍历序列的第一位,只要给定先序遍历序列,我们就能直接找到根结点。但是,如果只给出先序遍历序列,那么我们仅仅能够确定根结点,左右子树是没办法区分开的。要唯一确定一棵二叉树,还需要给定中序遍历序列。
对于中序遍历来说,总是先访问左子树,再访问根结点,最后访问右子树。因此,在二叉树的每一个结点数值均不同的情况下,只要遍历中序遍历序列,找到与先序遍历序列的第一位(即根结点)数值相同的结点,就可以确定该结点为根结点,进而中序遍历序列中区分出左子树和右子树。
代码
(买一送一,顺便附上后序遍历)
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 10000;
//声明node结构
struct node
{
int data; //结点数据
node* ltree; //结点的左子树根结点的指针
node* rtree; //结点的右子树根结点的指针
};
//分别为先序遍历序列、中序遍历序列和后序遍历序列
int pre[maxn], mid[maxn], pos[maxn];
/*通过先序序列和中序序列构建二叉树,并返回根节点地址。
各参数分别为:
先序序列第一个元素的索引、先序序列最后一个元素的索引、
中序序列第一个元素的索引、中序序列最后一个元素的索引、
*/
node* create1(int pre_l, int pre_r, int mid_l, int mid_r)
{
//先序序列长度不大于0时,直接返回
if(pre_l > pre_r)
return NULL;
//动态申请一个新的结点,并赋值为先序序列的第一个元素,作为当前根节点
node* root = new node;
root->data = pre[pre_l];
//遍历中序序列的索引
int k;
//找到根节点时,跳出循环,k就是根节点在中序序列中的索引
for(k = mid_l; k <= mid_r; k++)
{
if(mid[k] == root->data)
break;
}
//左子树的长度
int num_l = k - mid_l;
//递归创建当前根节点的左子树
root->ltree = create1(pre_l + 1, pre_l + num_l, mid_l, k - 1);
//递归创建当前根节点的右子树
root->rtree = create1(pre_l + num_l + 1, pre_r, k + 1, mid_r);
//返回当前根节点的地址
return root;
}
/*
//通过后序序列和中序序列构建二叉树,并返回根节点地址
node* create2(int pos_l, int pos_r, int mid_l, int mid_r)
{
if(pos_l > pos_r)
{
return NULL;
}
node* root = new node;
root->data = pos[pos_r];
int k;
for(k = mid_l; k <= mid_r; k++ )
{
if(mid[k] == root->data)
break;
}
int num_l = k - mid_l;
int num_r = mid_r - k;
root->ltree = create2( pos_l, pos_l + num_l - 1, mid_l, k - 1 );
root->rtree = create2(pos_l + num_l, pos_r - 1, k + 1, mid_r );
return root;
}
*/
int n, num = 0; //通过num控制结尾不输出空格
//层序遍历,采用广度优先探索的方法
void level_traversal(node* root)
{
//声明存放结点地址的队列
queue<node*> q;
//先把根节点送进队列,可以保证第一次判断不为空
q.push(root);
while(!q.empty())
{
//暂存队列首元素
node* tmp_node = q.front();
//输出队列首元素的值
cout<<tmp_node->data;
num++;
if(num < n)
cout<<" ";
//队列首元素读取完之后要弹出队列
q.pop();
//如果左子树的根节点不为空,则将左子树的根节点插入队列
if(tmp_node -> ltree != NULL)
q.push(tmp_node->ltree);
//如果右子树的根节点不为空,则将右子树的根节点插入队列
if(tmp_node -> rtree != NULL)
q.push(tmp_node->rtree);
}
}
int main()
{
//输入树的结点个数
cout<<"Input the number of nodes:";
cin>>n;
//输入先序遍历序列
cout<<"Input the preorder:";
for(int i = 0; i < n; i++)
{
cin>>pre[i];
}
/*
//输入后序遍历序列
cout<<"Input the postorder:";
for(int i = 0; i < n; i++)
{
cin>>pos[i];
}
*/
//输入中序遍历序列
cout<<"Input the inorder:";
for(int i = 0; i < n; i++)
{
cin>>mid[i];
}
node* root1 = create1(0, n - 1, 0, n - 1);
//输出层序遍历序列
cout<<"Level order:";
level_traversal(root1);
// node* root2 = create2(0, n - 1, 0, n - 1);
// BFS(root2);
return 0;
}
运行结果
假设有如一颗二叉树如下图所示:
输入该树的前序遍历序列和中序遍历序列,输出层序遍历序列结果如下:
程序运行结果正确。