上一期我们已经介绍了而二叉树的基础了,那么我们下面看一下更多的相关问题。
根据遍历构造二叉树
是不是每一种遍历组合都能构造出二叉树呢?答案是否。前序遍历和后序遍历的组合是不能确定为一二叉树的。前序和后序在本质上都是将父节点与子结点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子关系,而不能确定一个二叉树。
先看一个题:
根据先序遍历的特点可知,第一个字符一定是代表最顶端的父节点,之后我们要确定做分支和右分支的界限,就是如何区分左右节点。这时就要借助中序遍历,我们知道中序遍历是遍历两次才输出,也就是说先输出叶节点再输出根节点(相对),那么我们就可以找到相应的根节点。
代码:
struct node* build(char *a,char*b,int len)
{
node* root=new node;
int i;
if (len == 0)
return NULL;
root->data = a[0];//先找到最顶端父节点并定位
for (i = 0; i < len; i++)
{
if (b[i] == a[0])
{
break;//找到后接着退出。
}
}
root->l = build(a + 1, b, i);//运用递归向下搜索左枝叶中父节点
root->r = build(a + i + 1, b + i + 1, len - i - 1);//运用递归向下搜索右枝叶中父节点
return root;
}
解释一下build里面的参数含义:
指向左节点里面的含义:a+1含义是下一次递归到新的父节点;下一次递归i为新的序列长度,也就是新父节点下的长度,例如i=2,A下面的左边界定数共有两个,递归进行遍历。如果为空怎么办?那就返回NULL并退出,这样左半边的树就建好了。
指向右节点里面的含义:a+i+1含义是跳过左边的树直接从A右边的第一个节点E开始,len-i-1的含义相同,都是使指针指到右边的子树的第一个节点。
完整代码:
#include<iostream>
#include<cstring>
using namespace std;
char a[515555],b[515555];
int flag;
struct node
{
char data;
node* lc;
node* rc;
};
struct node* build(char *a,char*b,int len)
{
node* root=new node;//在外面定义
int i;
if (len == 0)
return NULL;
root->data = a[0];
for (i = 0; i < len; i++)
{
if (b[i] == a[0])
{
break;
}
}
root->lc = build(a + 1, b, i);
root->rc = build(a + i + 1, b + i + 1, len - i - 1);
return root;
}
void las(node* root)
{
if (root)
{
las(root->lc);
las(root->rc);
cout << root->data;
}
}
int main()
{
node* root;
while (cin >> a>>b)
{
int len;
len = strlen(a);
root = build(a, b, len);
las(root);
cout << endl;
}
return 0;
}
还有一种情况是根据中序遍历和后序遍历构造二叉树,后序遍历就是最后一个位置是二叉树的根节点,其次中序遍历第一个就是A下面的左节点,所以不用+1,其余相同。
struct node *build(int len, char* mid, char* las){
int i;
struct node* root;
root = new node;
if(len == 0){
root = NULL;
return root;
}
root->date = las[len - 1]; //找到当前树根节点
for (i = 0; i < len; i++){
if (las[len-1] == mid[i]){
break;
}
}
root->left = build(i, mid, las);
root->right = build(len - i - 1, mid + i + 1, las + i);
return root;
}
叶子问题
第一个问题是统计叶子数量:
再明确一遍叶子是什么:没有左右儿子。
怎么判断同时没有左右儿子:指向均为空。
void num(struct node *root)
{
if(root){
if(root->lc==NULL&&root->rc==NULL){
sum++;
}
num(root->lc);
num(root->rc);
}
}
第二个问题是输出叶子节点:
那我们就输出他的值域即可(如果没有顺序要求)
void num(struct node *root)
{
if(root){
if(root->lt==NULL&&root->rt==NULL){
cout<<root->data;
}
num(root->lt);
num(root->rt);
}
}
如果按照从上到下,从左到右的顺序应该怎么该呢?
要求是不是类似于层序遍历?这时候就要用到指针数组了。这里可以进行一下优化,给出大条件q[head]不为空,那么下面q[tail++]就可以直接进行,而不用两个if分别判断。
void yezi(struct node* root)//层序遍历
{
int head = 0, tail = 0;
struct node *q[100];
q[tail++] = root;
while (head < tail)
{
if (q[head])
{
if (!q[head]->lt && !q[head]->rt)
cout<<q[head]->c;
q[tail++] = q[head]->lt;
q[tail++] = q[head]->rt;
}
head++;
}
}
树的深度
由于左半边树和右半边树的长度通常不同,所以我们用max函数选出最大深度即可。约束递归条件是左或右边节点为空。
int high(node* root){
int h = 0;
if (root){
h = max(high(root->lc), high(root->rc))+1;
}
return h;
}
至此树的基本操作都已完成。