提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:以下是本篇文章正文内容,下面案例可供参考
tree=(k,r) k={ki|1<=i<=n;n>=0,ki∈elemtype} R={r}
其中,n为树中结点个数。若n=0,则为一棵空树,n>0时,称为一棵非空树。而关系r应满足下列条件:
有且仅有一个结点,没有前驱,称该结点为树根
除根节点以外,其余每个结点有且仅有一个直接前驱
树中每个结点可以有多个直接后继(孩子结点)
例题:
K={A,B,C,D,E,F,G,H,I,J,K,L,M}
R={r}
r={,,,,,
3. 只有一个根结点,其他结点称为根结点的子树.
4. 树的根结点没有前驱结点,除根结点外所有结点只有一个前驱结点,所有结点可以有0或多个后继结点;n个结点的树有n-1个边
(1)结点: 树的结点包含一个数据元素及若干指向其子树的分支
(2)度: 一个结点包含子树的数目,称为该结点的度。
(3)叶子(终端)结点:度为0的结点,称为叶子结点或树叶,也叫终端结点
(4)孩子结点:若结点X有子树,则子树的根结点为X的孩子结点,也称为孩子,儿子,子女等。
(5)双亲结点:若结点X有子女Y,则X为Y的双亲结点。
(6)祖先结点:从根结点到该结点所经过分支上的所有结点为该结点的祖先
(7)子孙结点:某一结点的子女及子女的子女都为该结点子孙。
(8)兄弟结点:具有同一个双亲的结点,称为兄弟结点。
(9)分支结点:除叶子结点外的所有结点,为分支结点,也叫非终端结点。(度不为0的结点)
(10)层数(层次):根结点的层数为1,其它结点的层数为从根结点到该结点所经过的分支数目再加1。
(11)树的高度(深度):树中结点所处的最大层数称为树的高度,如空树的高度为0,只有一个根结点的树高度1。
(12)树的度:树中结点度的最大值称为树的度。
(13)有序树:若一棵树中所有子树从左到右的排序是有顺序的,不能颠倒次序。称该树为有序树。
(14)无序树:若一棵树中所有子树的次序无关紧要,则称为无序树。
(15)森林(树林):若干棵互不相交的树组成的集合为森林。一棵树可以看成是一个特殊的森林。
(16)堂兄弟结点:在树中,双亲在同一层的那些结点,互成为堂兄弟结点。
(17)根往下走过的路径直到尾结点K,前面所有结点都是K的祖先结点,K是前面所有结点的子孙结点,路径上最接近K的结点叫K的双亲结点,K是其孩子结点,有相同双亲结点的叫兄弟结点
(18)树中一个结点的子结点(直接孩子结点)个数叫称为该结点的度,树中结点最大的度叫树的度
(19)度大于0的叫分支结点,又叫非终端结点,度为0的叫叶子结点(终端结点)
(20)结点的层次,深度是从根结点开始自顶向下逐渐累加;高度是从叶子结点自底向上累加;树的高度(深度)是树中结点最大的层数
(21)路径和路径长度:路径长度是所经过边的个数
(22)森林:是m>=0棵互不相交树的集合
4. 广义表表示法
(A(B(E(J,K,L),F),C(G),D(H(M),I)))
(1)树中结点数等于所有结点度数加1
(2)度为m的树中第i层上至多有m的i-1次幂个结点
(3)高度为h的m叉树至多有 个结点
(4)具有n个结点的m叉树最小高度为
满二叉树:深度为k具有2k-1个结点的二叉树,称为满二叉树。所有层都含最多个结点;对于满二叉树,对于编号为i的结点,若有双亲,则其双亲为i/2 ,若有左孩子为2i ,右孩子为 2i+1
完全二叉树:一个具有i个结点的深度为k的二叉树,他的每一个结点都与深度为k的满二叉树一 一对应,则称则棵二叉树为完全二叉树
若i<=n/2(向下取整)则结点i为分支结点,否则为叶子结点
叶子结点只可能在层次最大的两层上出现
如果有度为1的结点,则只能有1个,且该结点只有左孩子没有右孩子
按层序编号后,一旦出现某结点(编号为i)为叶子结点或只有左孩子,则编号大于i的结点均为叶子结点
二叉排序树:左子树上所有结点关键字小于根结点,右子树上所有关键字大于根结点,左子树和右子树又是一个二叉排序树
平衡二叉树:树上任意结点的左子树和右子树深度之差不超过1
二叉树的性质
(1)非空二叉树叶子结点数等于度为2的结点数+1,即N0=N2+1
(2)非空二叉树第k层上至多有2的k-1次幂个结点
(3)高度为H的二叉树至多有2的h次幂-1个结点
(4)当i>1时,结点i的双亲编号为 ,即当n为偶数,双亲编号为i/2,它是双亲的左孩子;i为奇数,双亲编号为(i-1)/2,它是双亲结点的右孩子
(5)2i<=N,结点的左孩子编号为2i,否则无左孩子
(6)2i+1<=N,结点i的右孩子编号为2i+1,否则无右孩子
(7)结点i所在层次为 log以2为底i的对数向下取整+1
(8)具有N个结点的完全二叉树高度为log以2为底N的对数向下取整+1
(9)深度为k的完全二叉树中最少有2的k-1次幂个结点,最多有2的k次幂-1个结点
中序遍历:左根右 中序分左右
先序遍历:根左右 先序找根
后序遍历:左根右
先序:ABDCEFG
中序:DBEFCGA
后序:DFEGCBA
中序:8 4 9 2 10 5 1 6 3 7
先序:1 2 4 8 9 5 10 3 6 7
后序:8 9 4 10 5 2 6 7 3 1
例题
中序:CBEDAGHFJI
后序:CEDBHGJIFA
中序:CDBFEAIHGJ
后序:DCFEBIHJGA
先序:ABCDEFGHIJ
void PreOrder(BiTree T){
if(T!=NULL){
visit(T->data); //如果输出则改成printf(“%d”,n);
preorder(T->lchild);
preorder(T->rchild);
}
}
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild);
visit(T->data); //输出时,改成printf(“%d”,n);
InOrder(T->rchild);
}
}
void PreOrder(BiTree T){
if(T!=NULL){
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T->data); //输出时,改成printf(“%d”,n);
}
}
时间与空间复杂度都是O(n)
度为0的条件语句
T->lchild==NULL && T->rchild==NULL
度为1的条件语句
(T->lchild!=NULL && T->rchild==NULL)||(T->lchild==NULL && T->rchild !=NULL)
度为2的条件语句
T->lchild!=NULL&&T->rchild!=NULL
void intravel (struct node *p)
{ q=p;top=0;bool=1;
while(bool)
{ while(q!=NULL)
{ top++;
s[top]=q;
q=q->lchild;
}
if(top==0) bool=0;
else{ q=s[top];
top--;
printf(“%c”,q->data);
q=q->rchild;
}
}
}
二叉树先序遍历的非递归算法:
void intravel (struct node *p)
{q=p;top=0;bool=1;
while(bool)
{while(q!=NULL)
{printf(“%c”,q->data);
top++;
s[top]=q;
q=q->lchild;
}
if(top==0) bool=0;
else{
q=s[top];
top--;
q=q->rchild;}
}
}
void lasttravel(struct node *p)
{q=p; top=0;bool=1;
while(bool)
{while(q!=NULL){ top++;
s[top]=q;
s2[top]=1;
q=q->lchild;}
if(top==0) bool=0;
else{if(s2[top]==1) {s2[top]=2; //第二次经过,不退栈
q=s[top];
q=q->rchild;}
else {q=s[top]; //第三次经过,退栈并且访问根节点
s2[top]=0;
top--;
printf(“%c”,q->data);
q=NULL; }
}
}
}
void leveltravel(struct node *p)
{struct node *q[20];
front =rear=0;
if(p!=NULL) {
rear++;
q[rear]=p;
}
while(front!=rear)
{
front++;
p=q[front];
printf(“%c”,p >data);
if(p->lchild!=NULL) {
rear++;
q[rear]=p->lchild;
}
if(p->rchild!=NULL) {
rear++;
q[rear]=p->rchild;
}
}
}
void PreOrder(BITree T)
{
if (T!=NULL)
{
printf(T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
void PreOrder(BITree T)
{
if (T!=NULL)
{if(T->lchild==NULL&&T->rchild==NULL)
printf(T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
int n=0;
void leafcount(BITree T)
{
if (T!=NULL)
{if(T->lchild==NULL&&T->rchild==NULL)
n++;
leafcount (T->lchild);
leafcount (T->rchild);
}
}
int PostTreeDepth(BITree T)
{
if (T!=NULL)
{
hl=PostTreeDepth (T->lchild);
hr=PostTreeDepth (T->rchild);
max=hl>hr ? hl:hr;
return(max+1);
}
else return 0;
}
struct node *creat()
{printf(“i,x=”); scanf(“%d,%d”,&i,&x);
while(i!=0&&x!=0)
q=(struct node *)malloc(sizeof(struct node));
q->data=x;
q->lch=NULL;
q->rch=NULL;
s[i]=q;
if(i==1) t=q;
else{ j=i/2;
if(i%2==0)s[j]->lch=q;
else s[j]->rch=q;}
scanf(“%d%d”,&i,&x); }
}
1.相关概念:
在一个n结点的链式存储二叉树中,有n+1个指针域是空指针域,可以把每个空指针域用于存放分别指向某种遍历次序的前趋和后继结点的指针。
线索:在结点的空指针域中存放的该结点在某遍历次序下的前趋结点和后继结点的指针叫做线索。
线索二叉树:对一个二叉树中的所有结点的空指针域按照某种遍历次序加线索的过程叫作线索化,被线索化了的二叉树称作线索二叉树。
在一个线索二叉树中,必须设法将线索与指向结点左、右儿子结点的指针加以区别。
可给每个结点增加两个标志域,即左线索标志域LTag,右线索标志域RTag。
2.基本概念:利用二叉树中大量空链域(N个结点中有N+1个空指针)存放直接前驱或后继的指针;线索化时规定,若无左子树,令lchild指向其前驱结点,若无右子树,rchild指向其后继结点
1. 二叉链表
通常每个结点中设置三个域,即值域、左指针域和右指针域,其结点结构如下:
其中data表示值域,用于存储放入结点的数据,lchild和rchild分别表示左指针域和右指针域,用以分别存储指向左儿子结点和右儿子结点的指针
先序创建二叉链表
Status CreateBiTree(BITree &T)
{ scanf(&ch);
if(ch==‘’) T=Null;
else
{ if (!(T=(BiTNode *)malloc(sizeof(BiTNode))))
exit (OVERFLOW);
T->data=ch;
CreateBiTree (T->lchild);
CreateBiTree (T->rchild);
}
return OK;
}
2. 三叉链表
通常每个结点中设置四个域,即值域、左指针域、右指针域和双亲指针域,其结点结构如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/030c77b91df145cdbecec7247fcafa69.png)
其中data表示值域,用于存储放入结点的数据,lchild和rchild分别表示左指针域和右指针域,用以分别存储指向左儿子结点和右儿子结点的指针,parent指向双亲结点
3. 链式存储结构
1) 二叉树链式存储结构表示
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild ,*rchild; //左、右孩子指针
} BiTNode,*BiTree;
在含有n个结点的二叉链表中含有n+1个空指针域
双亲表示法:这种方法用一组连续的空间来存储树中的结点,再保存每个结点的同时,附设一个指示其双亲结点在表中的位置,其双亲的结构如下:
孩子表示法:这种方法通常是把每个结点的孩子结点排列起来,构成一个单链表,称为孩子链表。n个结点共有n个孩子链表(叶结点的孩子链表为空表),而n个结点的数据和n个孩子链表的头指针又组成一个顺序表.
(1) 在所有兄弟结点之间加一连线;
(2) 对树中的每个结点,只保留其与第一个孩子结点之间的连线,删去其与其它孩子结点之间的连线。
(3) 以树的根结点为轴心,将整棵树顺时针旋转一定的角度,使之结构层次分明。
(1)将森林中的每棵树转换成相应的二叉树。
(2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,当所有二叉树连在一起后,所得到的二叉树就是由森林转换得到的二叉树
实例:
4. 二叉树转化为森林:二叉树根及其左子树为第一棵树的二叉树形式,二叉树根的右子树又可以看做是一个由除第一棵树外的森林转化后的二叉树,应用同样的方法,直到最后产生一棵没有右子树的二叉树为止,就得到了原森林
注:
(1)树转化为二叉树:在兄弟结点间加一条线;对每一个结点,只保留它与第一个子结点的连线,与其他子结点的连线全部抹掉;以树轴为轴心,顺时针旋转45°
(2)森林转化为二叉树:将每棵树根相连,将森林中每棵树转化为相应的二叉树;以第一棵树根轴心顺时针旋转45°
1.树的遍历
(1)先根遍历:与相应的二叉树的先序遍历相同
(2)后根遍历:与相应的二叉树的中序遍历相同
(3)层次遍历:与二叉树一样
2.森林的遍历
(1)先序遍历森林:访问森林每一棵树根结点;先序遍历第一棵树中根结点子树森林;先序遍历除去第一棵树之后剩余的树构成的森林
(2)中序遍历森林:中序遍历森林中第一棵树的根结点的子树森林;访问第一棵树根结点;中序遍历除去第一棵树之后剩余的树构成的森林
其中n表示叶子结点个数,wi和li分别表示叶子结点ki的权值和根到ki之间的路径长度。
给定一组n个实数,以它们作为各个叶子结点的权,可构成不同的有n个叶子结点的二叉树,这些二叉树的带权路径长度wpl可能不同。
3.哈夫曼树的定义:树中结点被赋予值(权),从树根结点到任意结点的路径长度(经过的边数)与该点上权值的乘积称为该结点的带权路径长度。树中所有叶结点的带权路径长度之和称为该树的带权路径长度,记为 式中,wi是第i个叶结点所带的权值;li是该叶结点到根结点的路径长度。在含有N个带权叶子结点的二又树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称为最优二叉树。
4.哈夫曼树又称最优二叉树,它是这样定义的:设有n个权值{w1,w2,…,wp},在这些权值为各个叶子结点的权构成的二叉树中,带权路径长度wpl,最小的二叉树叫做哈夫曼树
例如,下图甲的3棵二叉树,都有4个叶子结点a、b、c、d,分别带权7、5、2、4、它们的带权路径长度分别为
5. 哈夫曼树的构造:
(1)将这N个结点分别作为N棵仅含一个结点的二叉树,构成森林F
(2)构造一个新结点,并从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和
(3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中
(4)重复步骤(2)和(3),直至F中只剩下一棵树为止
(1)每个初始结点最终都成为叶结点,并且权值越小的结点到根结点的路径长度越大
(2)构造过程中共新建了N-1个结点(双分支结点),因此哈夫曼树中结点总数为2N-1
(3)每次构造都选择2棵树作为新结点的孩子,因此哈夫曼树中不存在度为1的结点
例如,权值{7},5,2,4}的哈夫受树的构造过程如图所示