二叉树的概念和建立【简单数据结构】(上)

二叉树是一种特殊的树,每次分叉不超过两部分。二叉树作为数据结构是非常重要和基础的。无论是二叉堆、线段树还是平衡树,这些高级的数据结构都以二叉树为基础。多叉树可以转换成二叉树,而更复杂的一些树结构会在后面高级中介绍。 

我们举个例子。

【题目问题】

        淘汰赛(1)。淘汰赛有2^{n}(n\leq7)个国家参加世界杯决赛圈而进入淘汰赛环节。已经知道各个国家的能力值,且都不相等。能力值高的国家和能力值低的国家踢比赛时高者获胜。1号国家和2号国家踢一场比赛,胜者晋级。3号国家和4号国家也踢一场,胜者晋级......晋级后的国家继续用相同的方式继续完成赛程,知道决出冠军。给出各个国家的能力值,请问:亚军是那个国家?

【题目分析】

        假设n=3,有8个国家参加,各个国家的能力值分别是(4,2,3,1,10,5,9,7)。可以很容易画出如下图所示的赛程图。

        从P1看出,最厉害的5号国家是当之无愧的冠军,但是1号国家实力不怎么样,只是在决赛之前碰到更弱的对手,所以侥幸闯进了决赛;而7号国家也挺厉害,但很不幸在半决赛碰到了5号国家所以惨遭淘汰。可见,并不是第二强的国家一定能拿到亚军。

        从P1还可以看到,从冠军(根节点)往下面看,每个获胜者节点下面都有2个国家。这就是一棵典型的满二叉树。更加严格的递归定义是:二叉树要么为空,要么为根节点、左子树、右子树构成,二左右子树分别还是一个二叉树(读者可以验证一下这个赛程图是否符合上面的定义)。如果一个结点没有任何子树,那就称为叶子节点。

        这个赛程图经过了3轮比赛,一共有4层节点,所以这个二叉树的高度是4.如果一个二叉树的高度为h,从第二层开始每一层的结点数都是上一层的两倍,一共有(2^{h}-1)个结点的二叉树称为完美二叉树。

        对于完美二叉树,可以从上到下,从左到右,对各个结点从1开始分配序号,如下P2所示。

        

                                             图P1   赛程图

      图P2     完美二叉树的结点编号↑

有没有发现一些规律?对于i号非叶子结点,它的左子树编号为2*i,右子树的编号是2*i+1.这样就可以创建若干足够大的数组将各个结点的信息记录进去,通过计算编号来访问左右子树,并使用递归的方式得到各个子树的统计,代码如下(代码结构如下,对错未调试,读者自行调试):

【输入样例】

8

4 2 3 1 10 5 9 7

【输出样例】 

1

【解题代码】 

#include<cstdio>
#include<iostream>
using namespace std;
int value[260],winner[260];
int n;
void dfs(int x){
	if(x>=1<<n){
		return;
	}
	else{
		dfs(2*x);
		dfs(2*x+1);
		int lvalue=value[2*x],rvalue=value[2*x+1];
		if(lvalue>rvalue){	//左结点获胜 
			value[x]=lvalue;//记录下获胜方的能力值
			winner[x]=winner[2*x];;//和获胜方的编号 
		}else{//右结点获胜 
			value[x]=rvalue;
			winner[x]=winner[2*x+1];
		}
	}
}
int main(){
	cin>>n;
	for(int i=0;i<1<<n;i++){
		cin>>value[i+(1<<n)];//读入各个结点的能力值
		winner[i+(1<<n)]=i+1;//叶子结点的获胜方就是自己国家的编号 
	}
	dfs(1);//从根节点开始遍历
	cout<<value[2]>value[3]?winner[3]:winner[2]; //找出亚军输出 
	return 0; 
}

        考虑到这是一个完美二叉树,1到(1<<n)-1编号都是非叶子结点,编号不小于1>>n都是叶子结点。本文使用value[i]来记录叶子结点的实力值,或者是非叶子结点的该子树的最大值;是由winner[i]来记录该子树的获胜者。当所有比赛模式完毕,winner[1]记录了整场比赛的冠军,value[i]记录冠军的实力值。这是要比较一下谁是决赛败者,输出败者的国家编号,即整场比赛的亚军。

        由于需要维护和子树相关的两个值——value和winner,所以建立了这两个数字存储字数信息。有时只需要维护子树的一个信息,也可不用建立这样的数组,而是将dfs函数定义为具有返回值函数,然后在递归函数中处理这个值。事实上函数也可以返回两个(甚至是多个)返回值,可以使用STL中的pair容器做到,这里不再详细阐述。

下一章继续讲解,这一章就此结束。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值