树的基本概念
-
树(tree),是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。
-
树是一种非线性的数据结构,用它能很好地描述有分支和层次特性的数据集合。
-
树是由n(n>0)个元素组成的有限集合,其中:
- 每个元素称为结点(node);
-
有一个特定的结点,称为根结点或树根 (root);
-
除根结点外,其余结点能分成m(m>=0)个互不相交的有限集合T0,T1,T2,……Tm−1。其中的每个子集又都是一棵树,这些集合称为这棵树的子树。
如下图是一棵典型的树:
树的结点
结点的层次:
根结点为第一层,根的子结点为第二层,依次向下递推…
树的深度:
树种结点的最大层次。eg:该树的深度为4 森林:互不相交的树称为森林
树结构的存储
我们表示一棵树的方法有:双亲表示法,孩子表示法,孩子兄弟表示法
双亲表示法
以双亲作为索引的关键词的一种存储方式
每个结点只有一个双亲,所以选择顺序存储占主要
结点定义:
struct Node{
char data;//存储值
int parent;//存储结点的父亲结点编号
}a[maxn];//结点个数maxn
孩子表示法
由于每个结点可有多个子树,所以我们用树的度数来定义每个结点的孩子数
结点定义:
struct Node{
char data;//存储值
int child[max_d];//树的度数是max_d
}a[maxn];//结点个数maxn
孩子兄弟表示法
任意一棵树,他的结点的第一个孩子如果存在就是唯一结点,他的右兄弟如果存在,也是唯一的,因此,我们设置两个指针,分别指向该结点的第一个孩子和该结点的右兄弟
结点定义:
struct Node{
char data;//存储值
Node *Firstchild,*Rightbrother;
}a;
二叉树
二叉树的定义
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
满二叉树
满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点有:
-
叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
-
非叶子结点的度一定是2。
-
在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
-
以下是一颗满二叉树:
完全二叉树
对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
完全二叉树的特点
-
叶子结点只能出现在最下层和次下层。
-
最下层的叶子结点集中在树的左部。
-
倒数第二层若存在叶子结点,一定在右部连续位置。
-
如果结点度为1,则该结点只有左孩子,即没有右孩子。
-
同样结点数目的二叉树,完全二叉树深度最小。
-
在完全二叉树中,具有n个结点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整。
若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:
- 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
- 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
- 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。
注:满二叉树一定是完全二叉树,但反过来不一定成立。
二叉树遍历
二叉树的遍历是指从二叉树的根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
二叉树的访问次序可以分为四种:前序遍历、中序遍历、后序遍历、层序遍历
前序遍历
先访问根结点,再从左到右按照前序遍历思想递归遍历各棵子树。 上图前序遍历的结果为:ABDHIEJCFG 从根结点出发,则第一次到达结点A,故输出A; 继续向左访问,第一次访问结点B,故输出B; 按照同样规则,输出D,输出H; 当到达叶子结点H,返回到D,此时D的左子树已访问结束,进而访问D的右子树I; 按照同样的访问规则,继续输出E,J,C,F,G;
中序遍历
先遍历左子树,再访问根结点,再遍历右子树。 上图中序遍历的结果为:HDIBJEAFCG 从根结点出发,访问结点A,A存在左子树B,则递归访问左子树B,依次递归直到H; 到达H,H左子树为空,则输出结点H,访问H右子树,为空,返回H子树根结点D; 返回至D,此时D的左子树访问完毕,输出D,访问D的右子树I; 结点I左子树为空,则输出I,右子树为空则返回父结点D; 按照同样规则继续访问,输出B,J,E,A,F,C,G;
后序遍历
先从左到右遍历各棵子树,再访问根结点。 上图后序遍历的结果为:HIDJEBFGCA 从根结点A出发,A左右子树非空,先递归访问左子树B 以此类推到达H,H左、右子树为空,则输出H; 由H返回至D,D左子树访问结束,递归访问其右子树I; I左右子树均为空,输出I; 返回至D,此时D左、右子树均访问结束,故输出D; 按照同样规则继续访问,输出J,E,B,F,G,C,A; 层次遍历: 按层次从小到大逐个访问,同一层次按照从左到右的次序。 上图层次遍历的结果为:ABCDEFGHIJ
例题
10.4.8.1 树的遍历
【问题描述】
给出一个n个结点的二叉树,请求出二叉树的前序遍历,中序遍历和后序遍历。
【输入格式】
第一位一个整数n(0<n<=26),表示二叉树有n个结点,结点序号:1∼n。
以下n行,第i行表示序号为i的结点信息,第一个大写字母表示结点的值,后面为两整数,第一个表示左儿子序号,第二个表示右儿子序号,如果该序号为0表示没有,结点1为根结点。
【输出格式】
共三行,第一行为二叉树的前序遍历,第二行为中序遍历,第三行为后序遍历
【输入样例】
7
F 2 3
C 4 5
E 0 6
A 0 0
D 7 0
G 0 0
B 0 0
【输出样例】
FCADBEG
ACBDFEG
ABDCGEF
【代码实现】
#include <bits/stdc++.h>
const int maxn=26+5;
struct Node{//结点
char data;//值
int lch,rch;//记录结点左右儿子序号
}a[maxn];
int n;
void Head_s(int x){//前序遍历
if(x==0)return;
printf("%c",a[x].data);//先输出根结点
Head_s(a[x].lch);//再递归访问左子树
Head_s(a[x].rch);//再递归访问右子树
}
void Mid_s(int x){//中序遍历
if(x==0)return;
Mid_s(a[x].lch);//先遍历左子树
printf("%c",a[x].data);//左子树访问结束,输出根结点
Mid_s(a[x].rch);//再递归访问右子树
}
void Tail_s(int x){//后序遍历
if(x==0)return;
Tail_s(a[x].lch);//先遍历左子树
Tail_s(a[x].rch);//再遍历右子树
printf("%c",a[x].data);//左右子树访问结束,输出根结点
}
void Solve(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf(" %c%d%d",&a[i].data,&a[i].lch,&a[i].rch);
Head_s(1);printf("\n");
Mid_s(1);printf("\n");
Tail_s(1);printf("\n");
}
int main(){
Solve();
return 0;
}
评论