一、树的定义和概念
定义
树是n(n≥0)个结点的有限集(n=0为空树)。
任意⼀一棵⾮非空树中满⾜足如下条件:
1.有且仅有⼀一个特定的称为根(root) 的结点
2.其余n-1个结点可以划分成m(m>0)个互不相交的有限集,称为根root的子树
表示方法:
树型表示法,文氏图表示法,括号表示法,凹入表示法
基本术语
结点:包含一个数据元素及若干指向其子树的分支
结点的度:结点拥有的子树个数
树的度:树内各结点度的最大值
叶子(终端结点):度为0的结点
分支结点、内部结点:度不为0的结点
结点的子树的根称为该结点的孩子
相应地,该结点称为孩子的双亲
同⼀双亲的孩子之间互称兄弟
其双亲在同一层的结点互为堂兄弟。
结点的祖先是从根到该结点经过的所有结点
以某结点为根的子树的任一结点称为该结点的子孙
树的深度(高度):结点的最大层次
有序树:树中各结点的子树从左到右有次序不可调换
路径:存在一个结点序列ki,ki1,ki2,…,kin,kj,使得序列列中除ki外 的任⼀结点都是其在序列列中的前⼀个结点的后继,称这结点序列(ki,ki1,ki2,…,kj)为由ki到kj的一条路径
路径的长度 等于路径所通过的结点数目减1(即路径上分支数目)。
树的三种运算:
1.寻找满足某种关系的结点
2.遍历
3.插入和删除
二、二叉树
定义
满足以下两个条件的树形结构:
1.每个结点的度都不大于2
2.每个结点的孩子结点次序不能随意颠倒(左孩子和右孩子)
性质
1.第i层最多有2^(i-1)个结点
2.深度为k的二叉树至多有2^k-1个结点(满二叉树)
3. 对任意一棵二叉树,若终端结点数为n0,度为 2的结点数为n2,则n0=n2+1
证明:设二叉树中结点总数为n,n1为二叉树中度为1 的结点总数,设二叉树中分支数目为B 。 ①n=n0+n1+n2
除根结点外,每个结点均对应一个进入它的分支: ②n=B+1
二叉树中的分支都是由度为1和度为2的结点发出 ③B=n1+2n2
完全二叉树:深度为k,结点为n的二叉树,结点1~n的位置序号与满二叉树的位置序号一一对应
满二叉树一定是完全二叉树
完全二叉树不一定是满二叉树
4.具有n个结点的完全二叉树的深度为 ⎣log2n⎦+1或⎡log2(n+1)⎤
5.:对完全二叉树中编号为i的结点,
(1)若i=1,则结点i是二叉树的根,无双亲;
若i>1,双亲结点的编号为⎣i/2⎦
(2)若有左孩子结点,则左孩子结点的 编号为2i;
若有右孩子结点,则右孩子 结点的编号为(2i+1)
(3)若n为奇数,每个分支结点都既有左孩子结点,也有右孩子结点;
若n为偶数,则编号最大的分支结点 (编号为n/2)只有左孩子结点,没有右孩子结点
存储
分为顺序存储结构和链式存储结构
顺序存储结构
#define MAX_TREE_SIZE 100;
typedef TElemtype SqBiTree[MAX_TREE_SIZE];
SqBiTree bt;
编号从小到大的顺序是存储单元的顺序
链式存储结构
typedef struct BiTNode{
struct BiTNode *lchild,rchild;
TElemtype data;
}BiTNode,*BiTree;
三、遍历二叉树
三种遍历⽅方法的递归定义
先序遍历(DLR)
操作过程: 若二叉树为空, 则空操作, 否则:
(1) 访问根结点
(2) 按先序遍历左子树;
(3) 按先序遍历右子树。
中序遍历(LDR)
操作过程: 若二叉树为空,则空操作,否则: (1) 按中序遍历左子树;
(2) 访问根结点;
(3) 按中序遍历右子树。
后序遍历(LRD)
操作过程: 若二叉树为空, 则空操作, 否则: (1) 按后序遍历左子树;
(2) 按后序遍历右子树;
(3) 访问根结点。
算术式的二叉树表示
前缀:(波兰表达式)
中缀
后缀:(逆波兰表达式)
遍历递归算法
typedef struct BiTNode{
struct BiTNode *lchild,*rchild;
TElemtype data;
}BiTNode,*BiTree;
//先序遍历
void PreOrderTraverse(BiTree T){
if(T!=NULL){
printf("%d",T->data);
}
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
//中序遍历
void InOrderTraverse(BiTree T){
if(T!=NULL){
InOrderTraverse(T->lchild);
printf("%d",T->data);
InOrderTraverse(T->rchild);
}
}
//后序遍历
void PostOrderTraverse(BiTree T){
if(T!=NULL){
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%d",T->data);
}
}
遍历非递归算法
typedef struct{
struct SqStack *base;
struct SqStack *top;
int stacksize;
}SqStack;
SqStack InitStack(SqStack &S){
S.base=(SqStack *)malloc(STACK_INT_SIZE*sizeof(SqStack));
S.base=S.top;
S.stacksize=STACK_INT_SIZE);
}
//先序
void PreOrderTraverse(BiTree T){
SqStack S;
S=InitStack(S);
Push(S,T);
while(!StackEmpty(S)){
BiTree p;
pop(S,p);
if(p->rchild) Push(S,p->rchild);
if(p->lchild) Push(S,p->lchild);
}
}
//中序
void InOrderTraverse(BiTree T){
SqStack S;
S=InitStack(S);
BiTree p=T;
while(p||!StackEmpty(S)){
if(p){
Push(S,p);
p=p->lchild;
}else{
Pop(S,p);
p=p->rchild;
}
}
}
//后序
创建二叉链表
按先序序列建立二叉链表:
void CreateBiTree(BiTree &T){
scanf(&ch);
if(T=='.') T=NULL;
else{
T=(BiTree)malloc(sizeof(BiTNode));
T->data=ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
四、线索二叉树
有2n-(n-1)=n+1个空链域,利用这些空链域存放前驱和后继的信息。
LTag:
0 lchild域指示结点的左孩⼦
1 lchild域指示结点的遍历前驱
RTag:
0 rchild域指示结点的右孩⼦
1 rchild域指示结点的遍历后继
指向前驱和后继结点的指针叫做线索。
以这种结构组成的二叉链表作为二叉树的存储结构,叫做线索链表。
对二叉树以某种次序进行遍历并且加上线索的过程叫做线索化。
线索化了的二叉树称为线索二叉树。
线索二叉树的数据结构
//线索二叉树
typedef struct BiThrNode{
TElemtype data;
int LTag,RTag;
struct BiThrNode *lchild,*rchild;
}BiThrNode,*BiThrTree;
访问线索二叉树
#### 中序线索二叉树查找前驱后继
//中序 直接前驱
BiThrTree PreNode(BiThrTree p){
pre=p->lchild;
if(p->LTag==0){
while(pre->RTag==0){
pre=pre->rchild;
}
return pre;
}
}
//中序 直接后继
BiThrTree PostNode(BiThrTree p){
post=p->rchild;
if(p->RTag==0){
while(post->LTag==0){
post=post->lchild;
}
}
return post;
}
中序遍历线索二叉树
后序线索二叉树查找前驱后继
/
/后序 直接前驱
BiThrTree PreNode(BiThrTree p){
if(p->LTag==1) pre=p->lchild;
else{
if(p->RTag==0) pre=p->rchild;
if(p->RTag==1) pre=p->lchild;
}
}
//后序 直接后继
BiThrTree PostNode(BiThrTree p){
//需要知道双亲
}