【挑战程序设计竞赛】树


从根r到结点x的路径长度为x的 深度(depth),结点x到叶结点的最大路径长度成为结点x的

有根树的表达

题目

请编写一个程序,输出给定有根树T中各节点u的信息。

思路

采用“左子右兄弟表示法”:
1、结点u的父节点
2、结点u最左侧的子结点
3、结点u右侧紧邻的兄弟结点

引用u.parent即可知道结点u的父结点,不存在父结点就是跟。不存在u.left的结点是叶子结点。不存在u.right的结点为最右侧子结点。

代码

#include <iostream>
using namespace std;
#define MAX 100005
#define NIL -1

struct Node{
	int p,l,r;
}; 

Node T[MAX];
int n,D[MAX];
/*打印*/
void print(int u){
	int i,c;
	cout<<"node"<<u<<":";
	cout<<"parent="<<T[u].p<<",";
	cout<<"depth="<<D[u]<<",";
	
	if(T[u].p==NIL) cout<<"root, ";
	else if(T[u].l==NIL) cout<<"leaf, ";
	else cout<<"internal node, ";
	
	cout<<"[";
	for(i=0,c=T[u].l;c!=NIL;i++,c=T[u].r){
		if(i) cout<<" ";
		cout<<c;
	} 
	cout<<"]"<<endl;
}
/*递归求深度*/
int rec(int u,int p){
	D[u]=p;
	if(T[u].r!=NIL) rec(T[u].r,p);//右侧兄弟设置为相同深度 
	if(T[u].l != NIL) rec(T[u].l,p+1); //最左侧子结点的深度设置为自己的深度+1 
}

int main(){
	int i,j,d,v,c,l,r;
	cin>>n;
	for(i=0;i<n;i++) T[i].p=T[i].l=T[i].r=NIL;
	
	for(i=0;i<n;i++){
		cin>>v>>d;
		for(j=0;j<d;j++){
			cin>>c;
			if(j==0) T[v].l=c;
			else T[l].r=c;
			l=c;
			T[c].p=v;
		}
	}
	for(i=0;i<n;i++){
		if(T[i].p==NIL) r=i;
	}
	
	rec(r,0);
	
	for(i=0;i<n;i++) print(i);
	
	return 0;
} 

二叉树的表达

题目

二叉树T,请编写一个程序,输出其各结点u的如下信息:
1、u的结点编号
2、u的深度
3、u的父结点
4、u的高
5、u的兄弟结点
6、结点的种类
7、u子结点数

思路

求二叉树结点的高可以用递归法,如果左右子树不为空,则高度加1,继续向下进行。最后选择左右子树高度中的最大值作为二叉树结点的高。

代码

/*输入:id left(左子树) right(右子树)
  输出:node id:parent=p,sibling=s,degree=d,depth=dep,height=h,type 
*/ 
#include <iostream>
#define MAX 1000
#define NIL -1
using namespace std; 
struct Node{
	int parent,left,right;
};

Node T[MAX];
int n,D[MAX],H[MAX];//子树的深度,子树的高度 
/*求深度(从根r到结点x的路径长度为x的深度)*/
void setDepth(int u,int d){
	if(u==NIL) return;
	D[u]=d;
	setDepth(T[u].left,d+1);
	setDepth(T[u].right,d+1); 
}
/*求高度(结点x到叶结点的最大路径长度成为结点x的高)*/
int setHeight(int u){
	int h1=0,h2=0;
	if(T[u].left!=NIL) h1=setHeight(T[u].left)+1;
	if(T[u].right!=NIL) h2=setHeight(T[u].right)+1;
	return H[u]=(h1>h2?h1:h2);
}
/*求兄弟结点*/ 
int getSibling(int u){
	if(T[u].parent==NIL) return NIL;
	if(T[T[u].parent].left!=u && T[T[u].parent].left!=NIL) return T[T[u].parent].left;
	if(T[T[u].parent].right!=u && T[T[u].parent].right!=NIL) return T[T[u].parent].right;
} 
/*打印*/
void print(int u){
	printf("node %d: ",u);
	printf("parent = %d: ",T[u].parent);
	printf("sibling = %d: ",getSibling(u));
	//求度数 
	int deg=0;
	if(T[u].left!=NIL) deg++;
	if(T[u].right!=NIL) deg++;
	printf("degree = %d: ",deg);
	printf("depth = %d: ",D[u]);
	printf("height = %d: ",H[u]);
	
	if(T[u].parent==NIL) printf("root\n");	
	else if(T[u].left==NIL &&T[u].right==NIL) printf("leaf\n");
	else printf("internal node\n");
}

int main(){
	int i,v,l,r,root=0;
	scanf("%d",&n);
	//初始化 
	for(i=0;i<n;i++) T[i].parent=NIL;
	//赋值 
	for(i=0;i<n;i++){
		scanf("%d %d %d",&v,&l,&r);
		T[v].left=l;
		T[v].right=r;
		if(l!=NIL) T[l].parent=v;
		if(r!=NIL) T[r].parent=v;
	}
	
	for(i=0;i<n;i++) if(T[i].parent==NIL) root=i;
	
	setDepth(root,0);
	setHeight(root);
	
	for(i=0;i<n;i++) print(i);
	
	return 0; 
}

树的遍历

题目

系统地访问树的所有结点:
1、按照结点、左子树、右子树前序遍历
2、按照左子树、结点、右子树中序遍历
3、按照左子树、右子树、结点后序遍历

思路

以preParse(u)为例,程序先访问u,然后执行preParse(T[u].left)访问u的左子树,处理完成后执行preParse(T[u].right)访问u的右子树。
同理,只要改变print(u)的位置就可以实现不同的算法。

代码

/*输入:id left right
  输出:PreOrder:
        InOrder:
		PostOrder: 
*/ 
#include <stdio.h>
#define MAX 10000
#define NIL -1

struct Node{
	int parent;
	int right;
	int left;
};
struct Node T[MAX];
int n;
/*前序遍历*/
void preParse(int u){
	if(u==NIL) return;
	printf("%d",u);
	preParse(T[u].left);
	preParse(T[u].right);
}
/*中序遍历*/
void orderParse(int u){
	if(u==NIL) return;
	orderParse(T[u].left);
	printf("%d",u);
	orderParse(T[u].right);
}
/*后序遍历*/
void postParse(int u){
	if(u==NIL) return;
	postParse(T[u].left);
	postParse(T[u].right);
	printf("%d",u);
}
int main(){
	int i,v,l,r,root;
	//初始化 
	scanf("%d",&n);
	for(i=0;i<n;i++) T[i].parent=NIL;
	//赋值 
	for(i=0;i<n;i++){
		scanf("%d %d %d",&v,&l,&r);
		T[v].left=l;
		T[v].right=r;
		if(l!=NIL) T[l].parent=v;
		if(r!=NIL) T[r].parent=v;
	}
	//找到根 
	for(i=0;i<n;i++) if(T[i].parent==NIL) root=i;
	
	printf("PreOrder:");
	preParse(root);
	printf("\n");
	
	printf("InOrder:");
	orderParse(root);
	printf("\n");
	
	printf("PostOrder:");
	postParse(root);
	printf("\n");
	
	return 0;
}

遍历的运用——树的重建

题目

根据已知树的前序遍历结果和中序遍历结果,得到后序遍历结果。

思路

PreOrder按照根—>左子树—>右子树的顺序递归遍历,InOrder按照左子树—>根—>右子树的顺序递归遍历。首先按PreOrder遍历的顺序依次访问各结点。访问过程中,我们能通过In得知各子树内InOrder遍历的顺序,从而重建以当前结点c为根的左子树和右子树。
比如当前结点为1(前序遍历),其在in中的位置(中序遍历)为3 2 5 4 6 【1】 8 7 9,那么当前树的根就是1,左右子树为 3 2 5 4 6和8 7 9。依次类推。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

int n,pos;
vector<int> pre,in,post;

void rec(int l,int r){
	if(l>=r) return;
	int root=pre[pos++];
	int m=distance(in.begin(),find(in.begin(),in.end(),root));
	//distance计算两个iterator之间的距离 
	//find函数有三个参数, 分别代表 (起点, 终点后一位, 要找的数),返回一个地址 
	rec(l,m);
	rec(m+1,r);
	post.push_back(root);
}

void solve(){
	int i;
	pos=0;
	rec(0,pre.size());
	for(i=0;i<n;i++){
		if(i) cout<<" ";
		cout<<post[i];
	}
	cout<<endl;
}
int main(){
	int k,i;
	cin>>n;
	
	for(i=0;i<n;i++){
		cin>>k;
		pre.push_back(k);
	}
	
	for(i=0;i<n;i++){
		cin>>k;
		in.push_back(k);
	}
	
	solve();
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值