寒假算法学习(二叉树)

二叉树的遍历

遍历的方式

先序遍历

先序遍历:又称先根遍历,从根节点开始,按照“根-左-右”的顺序依次访问各节点。即先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。

void preo(struct TreeNode* root) {  
    if (root == NULL) return; // 空节点不遍历
    printf("%d ", root->val); // 访问根节点  
    preo(root->left); // 遍历左子树  
    preo(root->right); // 遍历右子树  
}  

中序遍历:

中序遍历:又称中根遍历,从根节点开始,按照“左-根-右”的顺序依次访问各节点。即先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。

void inor(struct TreeNode* root) {  
    if (root == NULL) return; // 空节点不遍历
  
    inor(root->left); // 遍历左子树
    printf("%d ", root->val); // 访问根节点
    inor(root->right); // 遍历右子树  
}  

后序遍历:

后序遍历:又称后根遍历,从根节点开始,按照“左-右-根”的顺序依次访问各节点。即先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。

void post(struct TreeNode* root) {  
    if (root == NULL) return; // 空节点不遍历

    post(root->left); // 遍历左子树  
    post(root->right); // 遍历右子树
    printf("%d ", root->val); // 访问根节点  
}  

层序遍历:

层序遍历:是一种按照树的层级结构逐层访问节点的方式。

其具体步骤如下:

  1. 树的根节点开始。
  2. 根节点放入队列中。
  3. 循环执行以下步骤,直到队列为
    • 从队列中取出一个节点。
    • 访问该节点。
    • 如果该节点有左子节点,将左子节点放入队列。
    • 如果该节点有右子节点,将右子节点放入队列。
#include <bits/stdc++.h>  
  
using namespace std;  
  
struct TreeNode {  
    int val;  
    TreeNode* left;  
    TreeNode* right;  
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}  
};  
  
void level(TreeNode* root) {  
    if (root == NULL) {  
        return;  
    }  
      
    queue<TreeNode*> q;  
    q.push(root); // 将根节点入队  
      
    while (!q.empty()) {  
        TreeNode* tempNode = q.front(); // 出队,获取队首元素  
        cout << tempNode->val << " "; // 打印当前节点的数据  
        q.pop(); // 出队后从队列中移除该节点  
          
        if (tempNode->left != NULL) { // 如果左子节点存在,则入队左子节点  
            q.push(tempNode->left);  
        }  
        if (tempNode->right != NULL) { // 如果右子节点存在,则入队右子节点  
            q.push(tempNode->right);  
        }  
    }  
}

遍历的使用

先序+中序

例题:P1827 [USACO3.4] 美国血统 American Heritage - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 $26$ 个的顶点。

这是在样例输入和样例输出中的树的图形表达方式:

         C
         /  \
        /  \
       B    G
      / \  /
       A   D  H
        / \
       E   F

附注:

- 树的中序遍历是按照左子树,根,右子树的顺序访问节点;
- 树的前序遍历是按照根,左子树,右子树的顺序访问节点;
- 树的后序遍历是按照左子树,右子树,根的顺序访问节点。

输入格式

第一行一个字符串,表示该树的中序遍历。

第二行一个字符串,表示该树的前序遍历。

输出格式

单独的一行表示该树的后序遍历。

样例输入
ABEDFCHG
CBADEFGH

样例输出
AEFDBHGC

思路:

利用先序遍历确定根节点,再拆分中序遍历,并记录左右长度,如果左右长度不为0,则继续拆分;递归地调用out函数处理左子树和右子树;在递归返回后,输出根节点。

代码:

#include<bits/stdc++.h>
using namespace std;

void out(string in,string pr){
	char root=pr[0];		//先序遍历最先遍历根节点,所以用一个字符记录根节点
	int l,r;
	l=in.find(root);		//在中序遍历中找出根节点
	r=in.size()-l-1;		//并将中序遍历拆分左右,用两个数记录左右字符长度
//如果左右字符长度不为 0,那么就继续拆分
	if(l){
		string s1_l=in.substr(0,l),s2_l=pr.substr(1,l);    //拆分
		out(s1_l,s2_l);
	}
	if(r){
		string s1_r=in.substr(l+1,r),s2_r=pr.substr(l+1,r);    //拆分
		out(s1_r,s2_r);
	}
	printf("%c",root);		//题目要求输出后序遍历
}
int main(){
	string s1,s2;
	cin>>s1>>s2;	//输入中序和先序遍历,注意顺序,先输入中序,再输入先序
	out(s1,s2);
	return 0;
}

中序+后序

例题:P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数)。

输入格式

共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。

输出格式

共一行一个字符串,表示一棵二叉树的先序。

样例输入
BADC
BDCA
样例输出
ABCD

思路: 同美国血统差不多

代码:

#include<bits/stdc++.h>
using namespace std;
void pre(string in,string out){
	char root=out[out.size()-1];	//后序遍历的最后一个字符,即根节点
	int l,r;						//记录中序遍历的左子树和右子树的长度
	l=in.find(root);				//找出根节点在中序遍历中的位置
	r=in.size()-l-1;				//计算右子树的长度

	printf("%c",root);		//题目要求输出先序遍历
	if(l!=0){
		string s1_l=in.substr(0,l),s2_l=out.substr(0,l);	//如果左子树或右子树的长度不为0(即存在非空子树),则递归拆分
		pre(s1_l,s2_l);									//递归地调用pre函数处理左子树和右子树
	}
	if(r!=0){
		string s1_r=in.substr(l+1,r),s2_r=out.substr(l,r);
		pre(s1_r,s2_r);
	}
}
int main(){
	string s1,s2;
	cin>>s1>>s2;	//需要注意的是,输入的顺序是先中序遍历,再后序遍历
	pre(s1,s2);
	return 0;
}

先序遍历和后序遍历,先序遍历和层序遍历,后序遍历和层序遍历,均不能确定唯一二叉树。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值