一般树的遍历(算法笔记)

本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。


一、树的静态写法

本节讨论的“树”是指一般意义上的树,即子结点个数不限且子结点没有先后次序的树,而不是上文中讨论的二叉树。
首先来回顾二叉树的结点的定义,可以注意到它是由数据域和指针域组成的,其中左指针域指向左子树根结点的地址,右指针域指向右子树根结点的地址。借鉴这种定义方法,对一棵一般意义的树来说,可以仍然保留其数据域的含义,而令指针域存放其所有子结点的地址(或者为其开一个数组,存放所有子结点的地址)。不过这听起来有点麻烦,所以还是建议在考试中使用其静态写法,也就是用数组下标来代替所谓的地址。但这样可能会导致内存使用超过题目限制,因此可以采用vector模板来存储结点。(摘自算法笔记)

二、树的先根、后跟和层次遍历

区别于二叉树,树没有左右子树一说,因此在DFS中,树只有先根和后根遍历,区别在于根和子节点的先后访问顺序,子节点代表岔道口,当不存在结点时即代表走到了死胡同。
树的层次遍历就是BFS,这个没有什么不同。通过例题来感受:
在这里插入图片描述
在保证除输出序列改变而题干不变的情况下,分别采用先根、后根和层次遍历实现,用静态写法实现代码如下,注意递归边界的体现
DFS:

#include<cstdio>  
#include<vector>
using namespace std;

const int MAXN = 51;
struct Node{
	vector <int> child;
}node[MAXN];
int count = 1 , n;

void preOrder(int root){    //先根 
	printf("%d", root);
	if(count <= n - 1) {
		printf(" ");	
		count++;
	}
	for(int i = 0; i < node[root].child.size(); i++)   //第二个表达式就是递归边界 
		preOrder(node[root].child[i]);
}

void postOrder(int root){  //后根 
	for(int i = 0; i < node[root].child.size(); i++)   //第二个表达式就是递归边界 
		preOrder(node[root].child[i]);
	printf("%d", root);
	if(count <= n - 1) {
		printf(" ");	
		count++;
	}
}

int main(){
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++){
		int k;
		scanf("%d", &k);
		for(int j = 0; j <= k - 1; j++){
			int temp;
			scanf("%d", &temp);
			node[i].child.push_back(temp);
		}	
	}
	preOrder(0);
}

BFS:

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;

const int MAXN = 51;
struct Node{
	vector <int> child;
}node[MAXN];
int count = 1 , n;

void BFS(int root){
	queue <int> qe;
	qe.push(root);
	while(!qe.empty()){
		int top = qe.front();
		qe.pop();
		printf("%d", top);
		if(count <= n - 1) {
			printf(" ");
			count++;
		}
		for(int i = 0; i < node[top].child.size(); i++)
			qe.push(node[top].child[i]);
	}
}

int main(){
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++){
		int k;
		scanf("%d", &k);
		for(int j = 0; j <= k - 1; j++){
			int temp;
			scanf("%d", &temp);
			node[i].child.push_back(temp);
		}	
	}
	BFS(0);
}

三、其余小题练习

1.树的高度
在这里插入图片描述
树是带有递归性质的,所以求树的高度就是求最大递归深度,用计数器即可实现,完整代码如下:

#include<cstdio>  
#include<vector>
using namespace std;

const int MAXN = 51;
struct Node{
	vector <int> child;
}node[MAXN];
int count = 1 , n;
int maxans = -1;

void preOrder(int root , int index){
	if((int)node[root].child.size() == 0 && maxans < index) maxans = index;  //这就是递归边界,只是隐含在了下面的循环中,最好还是写上这句 
	for(int i = 0; i < node[root].child.size(); i++)   //第二个表达式就是递归边界 
		preOrder(node[root].child[i] , index + 1);
}

int main(){
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++){
		int k;
		scanf("%d", &k);
		for(int j = 0; j <= k - 1; j++){
			int temp;
			scanf("%d", &temp);
			node[i].child.push_back(temp);
		}	
	}
	preOrder(0 , 1);
	printf("%d", maxans);
}

2.树的结点层号
在这里插入图片描述
老问题了,可以采用BFS+计数器的方式实现,也可以采用DFS+计数器的方式实现,把计数器放到结构体里就好,每向下一层就动态更新层号,完整代码如下:

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;

const int MAXN = 51;
struct Node{
	vector <int> child;
	int step;
}node[MAXN];
int count = 1 , n;

void BFS(int root){
	queue <int> qe;
	qe.push(root);
	node[root].step = 1;
	while(!qe.empty()){
		int top = qe.front();
		qe.pop();
		for(int i = 0; i < node[top].child.size(); i++){
			qe.push(node[top].child[i]);
			node[node[top].child[i]].step = node[top].step + 1;
		}
	}
}

int main(){
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++){
		int k;
		scanf("%d", &k);
		for(int j = 0; j <= k - 1; j++){
			int temp;
			scanf("%d", &temp);
			node[i].child.push_back(temp);
		}	
	}
	BFS(0);
	for(int i = 0; i <= n - 1; i++){
		printf("%d", node[i].step);
		if(i < n - 1) printf(" ");
	}
}

3.树的路径和
在这里插入图片描述
记录走过的路径权值和,求到达叶结点时本条路径上的权值和,将所有叶结点的权值和相加即可。采用计数器实现,每进入一个子结点就将原有路径上的权值和传入子节点。完整代码如下:

#include<cstdio>  
#include<vector>
using namespace std;

const int MAXN = 51;
struct Node{
	int data;
	vector <int> child;
}node[MAXN];
int n , ans = 0;

void preOrder(int root , int w){
	if((int)node[root].child.size() == 0) ans += w;
	for(int i = 0; i < node[root].child.size(); i++)   //第二个表达式就是递归边界 
		preOrder(node[root].child[i] , w + node[node[root].child[i]].data);
}

int main(){
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++)
		scanf("%d", &node[i].data);
	for(int i = 0; i <= n - 1; i++){
		int k;
		scanf("%d", &k);
		for(int j = 0; j <= k - 1; j++){
			int temp;
			scanf("%d", &temp);
			node[i].child.push_back(temp);
		}	
	}
	preOrder(0 , node[0].data);
	printf("%d", ans);
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓦耶_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值