一、概念
二叉树是一种特殊的树,每次分叉不超过2次,是非常重要和基础的数据结构;
如果一个结点没有任何子树,则该结点称为叶子结点;
如果一个二叉树高度为h,从第二层开始每一层结点数都是上一层的2倍,一共有2的k次方-1个结点的二叉树为完美二叉树(满二叉树),如下图;
![](https://img-blog.csdnimg.cn/img_convert/658e3f93657e5484d11d98e11093b08f.jpeg)
如果一个结点除了最后一层外外,其他层都是满的,而且最后一层的结点是从左到右的一排连续的,那么这样的二叉树称为完全二叉树,如下图·
![](https://img-blog.csdnimg.cn/img_convert/371d9ec1f554787596f070b1c3a1c1f9.jpeg)
二、二叉树的建立
例:
洛谷 P4715 【深基16.例1】淘汰赛
有 2的n(n≤7) 次方个国家参加世界杯决赛圈且进入淘汰赛环节。已经知道各个国家的能力值,且都不相等。能力值高的国家和能力值低的国家踢比赛时高者获胜。1 号国家和 2 号国家踢一场比赛,胜者晋级。3 号国家和 4 号国家也踢一场,胜者晋级……晋级后的国家用相同的方法继续完成赛程,直到决出冠军。给出各个国家的能力值,请问亚军是哪个国家?
对于完美二叉树,若i号结点非叶子结点,则它的左子树编号为2*i,右子树编号为2*i+1;
可以创建若干个数组记录个结点信息,通过计算编号访问左右子树,并使用递归的方式得到各个子树的统计;
#include<iostream>
#include<cstdio>
using namespace std;
int valun[260],winner[260];
int n;
void dfs(int x){
if(x>=i<<n){//若是叶子结点则不继续遍历
return ;
}
else{
dfs(2*x);//遍历左子树
dfs(2*x+1);//遍历右子树
int lvalun=valun[2*x],rvalun=valun[2*x+1];
if(lvalun>rvalun){//左结点获胜
valun[x]=lvalun;//记录获胜方能力值
winner[x]=winner[2*x];//和编号
}
else{//右结点获胜
valun[x]=rvalun;
winner[x]=winner[2*x+1];
}
}
}
int main(){
cin>>n;
for(int i=0;i<1<<n;i++){
cin>>valun[i+(1<<n);
winner[i+(1<<n)]=i+1;//叶子结点的获胜方是自己国家的编号
}
dfs(1);//从根节点开始遍历
cout<<valun[2]>valun[3]?winner[3]:winner[2];//找亚军
return 0;
}
三、二叉树的遍历
遍历是指沿着某一搜索路线,依次对树中的每一结点做一次且仅做一次访问
![](https://img-blog.csdnimg.cn/img_convert/7ed2a89f04e512505f4f8c69e7c175db.jpeg)
层次遍历
对二叉树进行广度优先搜索;将根结点放入队列中,取出每次入队的结点,即可得到层次遍历
从上图可看出,这棵二叉树的层次遍历是:1 2 3 4 5 6
深度优先遍历
先序遍历:首先遍历根结点,其次遍历左子树,最后遍历右子树
void pre_orde(int x){
cout<<x<<endl;
if(t[x].left)pre_orde(t[x].left);
if(t[x].right)pre_orde(t[x].right);
}
上图先序遍历:1 2 4 5 3 6
中序遍历:首先遍历左子树,其次遍历根结点,最后遍历右子树
void pre_orde(int x){
if(t[x].left)pre_orde(t[x].left);
cout<<x<<endl;
if(t[x].right)pre_orde(t[x].right);
}
上图中序遍历:4 2 5 1 6 3
后序遍历: 首先遍历左子树,其次遍历右子树,最后遍历根结点
void pre_orde(int x){
if(t[x].left)pre_orde(t[x].left);
if(t[x].right)pre_orde(t[x].right);
cout<<x<<endl;
}
上图后序遍历:6 3 5 4 2 1