赞
踩
二叉树
T
T
T:一个有穷的结点的集合。
这个结合可以为空
如不为空,则它由根结点和称为其 左子树
T
L
T_L
TL 和 右子树
T
R
T_R
TR 的两个不相交的二叉树组成。
二叉树具体五种基本形态。
二叉树的子树有左右顺序之分
特殊二叉树
斜二叉树(Skewed Binary Tree):只有左(右)子树的二叉树
完美二叉树(Perfect Binary Tree)或满二叉树(Full Binary Tree)
一个二叉树第 i i i层的最大结点数为: 2 i − 1 , i ≥ 1 2^{i-1},i \geq 1 2i−1,i≥1。
深度为 k k k的二叉树有最大结点总数为: 2 k − 1 , k ≥ i 2^{k-1},k \geq i 2k−1,k≥i
对任意非空二叉树
T
T
T,若
n
0
n_0
n0表示叶结点的个数,
n
1
n_1
n1表示度为1的非叶结点,
n
2
n_2
n2表示度为2的非叶结点的个数那么两者满足关系
n
0
=
n
2
+
1
n_0=n_2+1
n0=n2+1。
从边的角度考虑,边的总数:
所以有:
n
0
+
n
1
+
n
2
=
0
×
n
0
+
1
×
n
1
+
2
×
n
2
n_0+n_1+n_2=0 \times n_0+1 \times n_1+2 \times n_2
n0+n1+n2=0×n0+1×n1+2×n2
化简得:
n
0
=
n
2
+
1
n_0=n_2+1
n0=n2+1
顺序存储结构
链式存储结构
struct TreeNode{
ElementType Data;
struct TreeNode *Left;
struct TreeNode *Right;
};
typedef struct TreeNode *BinTree;
二叉树遍历的核心问题:二维结构的线性化
先序遍历
递归实现:
void PreOrderTraversal(BinTree BT)
{
if(BT)
{
printf("%d",BT->Data);//先访问根结点
PreOrderTraversal(BT->Left);//先序遍历左子树
PreOrderTraversal(BT->Right);//先序遍历左子树
}
}
非递归实现(堆栈实现):
void PreOrderTraversal(BinTree BT) { BinTree T=BT; Stact S = CreatStack(MaxSize);//创建并初始化堆栈 while( T || !IsEmpty(S) ) { //一直向左并将沿途结点压入堆栈 while(T) { printf("%5d",T->Data);//(访问)打印结点 Push(S,T); T=T->Left; } if(!IsEmpty(S)) { T=Pop(s);//结点弹出堆栈 T=T->Right;//转向右子树 } } }
例如:
遍历顺序:A B D F E C G H I
中序遍历
递归实现:
void InOrderTraversal(BinTree BT)
{
if(BT)
{
InOrderTraversal(BT->Left);//中序遍历其左子树
printf("%d",BT->Data);//先访问根结点
InOrderTraversal(BT->Right);//中序遍历其右子树
}
}
非递归实现(堆栈实现):
void InOrderTraversal(BinTree BT) { BinTree T=BT; Stact S = CreatStack(MaxSize);//创建并初始化堆栈 while( T || !IsEmpty(S) ) { //一直向左并将沿途结点压入堆栈 while(T) { Push(S,T); T=T->Left; } if(!IsEmpty(S)) { T=Pop(s);//结点弹出堆栈 printf("%5d",T->Data);//(访问)打印结点 T=T->Right;//转向右子树 } } }
例如:
遍历顺序:D B E F A G H C I
后序遍历
递归实现:
void PostOrderTraversal(BinTree BT)
{
if(BT)
{
PostOrderTraversal(BT->Left);//后序遍历其左子树
PostOrderTraversal(BT->Right);//后序遍历其右子树
printf("%d",BT->Data);//先访问根结点
}
}
非递归实现(堆栈实现):
后序遍历的顺序是左结点–右结点–根结点,用额外的一个堆栈按顺序存储结点,最后在将所有结点出栈并访问,因为堆栈有先进后出的特点,所有进栈的顺序为根结点–右结点–左结点。观察先序遍历的非递归实现,可以知道访问结点顺序为根结点–左结点–右结点,与进栈顺序差别只是左右结点的访问顺序不同,所以可以利用先序遍历的算法,只要先遍历右子树再遍历左子树,这样结点访问顺序就变成了根结点–右结点–左结点,在把访问用进栈代替,就可以用额外的一个堆栈按照根结点–右结点–左结点的顺序进栈,将所有结点进栈之后,依次出栈,并访问,就会按照左结点–右结点–根结点的顺序访问结点了。
void PostOrderTraversal(BinTree BT) { BinTree T=BT; Stact S1 = CreatStack(MaxSize);//创建并初始化堆栈S1 Stact S2 = CreatStack(MaxSize);//创建并初始化堆栈S2,用于按照顺序存储所有结点 while( T || !IsEmpty(S1) ) { //一直向右并将沿途结点压入堆栈 while(T) { Push(S2,T);//进栈,用于存储所有结点 Push(S1,T); T=T->Right; } if(!IsEmpty(S1)) { T=Pop(S1);//结点弹出堆栈 T=T->Left;//转向左子树 } } //出栈并访问 while(!IsEmpty) { T=Pop(S2); printf("%d\n",T->Data); } }
例如:
遍历顺序:D E F B H G I C A
观察可以发现:先序、中序和后序遍历过程中经过结点的路线是一样的,只是访问各结点的时机不同。
层序遍历
从结点访问其左、右结点
访问左儿子后,右儿子结点怎么办?
队列实现:遍历从根结点开始,首先将根结点入队,然后开始执行循环:结点出队、访问该结点、其左右儿子入队。
基本过程:先根结点入队,然后:
void LevelOrderTraversal(BinTree BT) { Queue Q; BinTree T; //如果树是空的则直接返回 if(!BT) { return; } Q=CreatQueue(MaxSize);//创建并初始化队列Q AddQ( Q , BT );//将根结点入队 while(!IsEmptyQ(Q)) { T=Delete(Q); printf("%d\n",T->Data);//访问取出队列的结点 if(T->Left) { AddQ(Q,T->Left); } if(T->Right) { AddQ(Q,T->Right); } } }
输出二叉树中的叶子结点
在二叉树的遍历算法中增加检测结点的“左右子树是否都为空”。
void PreOrderTraversal(BinTree BT)
{
if(BT)
{
if(!BT->Left&&!BT->Right)
{
printf("%d",BT->Data);//在先序遍历的代码基础上,给访问加个条件,只有叶子结点才访问。
}
PreOrderTraversal(BT->Left);//先序遍历左子树
PreOrderTraversal(BT->Right);//先序遍历左子树
}
}
求二叉树的高度
二叉树的告诉等于左、右子树高度最大的那个加一。
int PostOrderGetHeight(BinTree BT)
{
int HL=0,HR=0,MaxH=0;
if(BT)
{
HL=PostOrderGetHeight(BT->Left);//求左子树深度
HR=PostOrderGetHeight(BT->Right);//求右子树深度
MaxH=(HL>HR)?HL:HR;//取左右子树较大的深度
return (MaxSize+1);//返回树的告诉
}
else
{
return 0;//空树返回深度为0
}
}
二元运算表达式树及其遍历
二元运算表达式树:叶结点是运算数,非叶结点是运算符号,用于描述一个表达式。
由两种遍历确定二叉树
问:已知三种遍历中的任意两种遍历序列,能否唯一确定一棵二叉树呢?
答:不一定,两种遍历序列里必须要有中序遍历才行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。