树的构造、遍历

        一个二叉树,树中每个节点的权值互不相同。利用树的后序遍历和中序遍历,输出它的层序遍历。这是一个经典的树的构造,代码需要牢记。

题目

树的遍历

         树的遍历大致分为四种,先序遍历、中序遍历、后序遍历、层次遍历

先序遍历

        先访问根节点,再遍历左子树,最后遍历右子树。

        如下图,先序序列为ABDGCEF。

中序遍历

        先遍历左子树,再访问根节点,最后遍历右子树。

        如下图,中序遍历为DGBAECF。

     

后序遍历

        先遍历左子树,再遍历右子树,最后访问根节点。

        如下图,后序遍历为GDBEFCA。

 

层次遍历

        不同于前面三种,一层一层地访问二叉树中的所有结点。

        如下图,层次遍历为ABCDEFG。

解题思路

        根据如上图,可以看出后序遍历的最后一个,一定是根节点。所以我们在中序遍历中查找到根结点的位置,在根节点的左边就是左子树,右边就是右子树(如图),这样我们就可以用递归查找到每一个点。

         如题目提供的样例,可以画出如下图

        在书写代码中最难的可能就是递归的参数,我们设在中序序列中根节点的位置为k,所以在中序序列中左子树的范围为[il, k - 1],右子树的范围为[k + 1, ir]。在后序序列中我们将左右子树的交界设为x,而不管在中序序列还是后序序列中左子树都是相等,右子树也都是相等的,所以我们就可以列出方程:(k - 1) - il = x - pl,这样就可以得出后序序列中左子树的范围为[pl, pl + k - 1 - il],而右子树的范围为[pl + k - 1 - il + 1,  pr - 1]。 

 题解

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 40;
int n;
int po[N], io[N];        // po为后序遍历, io为中序遍历
int l[N], r[N];          // l存储左子树,r存储右子树

// 构造树 pl、pr为后序序列左右端点 il、ir为中序序列左右端点
int build(int pl, int pr, int il, int ir){
	int root = po[pr];
	int i, k = 0;

    // 查找到根节点的下标
	for(i = 0; i < n; i++){
		if(io[i] == root)
			break;
	} 
	k = i;
	if(il < k)
		l[root] = build(pl, pl + k - 1 - il, il, k - 1);
	if(ir > k)
		r[root] = build(pl + k - 1 - il + 1, pr - 1, k + 1, ir);
	return root;
}

// 利用bfs输出层次遍历的结果
void bfs(int root){
	queue<int> q;
	q.push(root);

	while(q.size()){
		int t = q.front();
		q.pop();
		cout << t << " ";
		if(l[t] != 0)
			q.push(l[t]);
		if(r[t] != 0)
			q.push(r[t]);
	}
} 

int main(){
	cin >> n;
	for(int i = 0; i < n; i++)
		cin >> po[i];
	for(int i = 0; i < n; i++)
		cin >> io[i];
	int root = build(0, n - 1, 0, n - 1);
	bfs(root);
	return 0;
} 

优化算法

         而我们还可以用哈希表存储中序序列的下标来优化查找根结点下标这一步骤,因为unordered_map的时间复杂度为O(1)。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

const int N = 40;
int n;
int po[N], io[N];
unordered_map<int, int> l, r, pos;

int build(int pl, int pr, int il, int ir){
	int root = po[pr];
	int k = pos[root];
	if(il < k)
		l[root] = build(pl, pl + k - 1 - il, il, k - 1);
	if(ir > k)
		r[root] = build(pl + k - 1 - il + 1, pr - 1, k + 1, ir);
	return root;
}

void bfs(int root){
	queue<int> q;
	q.push(root);
	while(q.size()){
		int t = q.front();
		cout << t << " ";
		q.pop();
		if(l.count(t))
			q.push(l[t]);
		if(r.count(t))
			q.push(r[t]);
	}
}

int main(){
	cin >> n;
	for(int i = 0; i < n; i++)
		cin >> po[i];
	for(int i = 0; i < n; i++){
		cin >> io[i];
		pos[io[i]] = i;        // 用哈希表来存储中序序列的下标
	}
	int root = build(0, n - 1, 0, n - 1);
	bfs(root);
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值