再谈二叉树(下)

上一期我们已经介绍了而二叉树的基础了,那么我们下面看一下更多的相关问题。

根据遍历构造二叉树

是不是每一种遍历组合都能构造出二叉树呢?答案是否。前序遍历和后序遍历的组合是不能确定为一二叉树的。前序和后序在本质上都是将父节点与子结点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子关系,而不能确定一个二叉树。

先看一个题:

根据先序遍历的特点可知,第一个字符一定是代表最顶端的父节点,之后我们要确定做分支和右分支的界限,就是如何区分左右节点。这时就要借助中序遍历,我们知道中序遍历是遍历两次才输出,也就是说先输出叶节点再输出根节点(相对),那么我们就可以找到相应的根节点。

代码:

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;
}

至此树的基本操作都已完成。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值