树、二叉树的基本概念

树基本概念

非线性结构,一个直接前驱,但可能有多个直接后继(1:n)
树的定义具有递归性,即树中还有树
根 叶子 森林
有序树 无序树
双亲 孩子 兄弟 堂兄弟 祖先 子孙
结点 结点的度 结点的层次 终端结点 分支结点
树的度 所有结点度中的最大值(Max{各结点的度}
树的深度指所有结点中最大的层数(Max{各结点的层次}
(或高度)
关于子树不相交的说明
树的表示法
图形表示法
广义表表示法
左孩子-右兄弟表示法
双亲孩子表示法
树的逻辑结构
一对多(1:n),有多个直接后继(如家谱树、目录树等等),但只有一个根结点,且子树之间互不相交。
广义表表示法
左孩子-右兄弟表示法

树的存储
顺序存储、链式存储

二叉树
1、基本概念
二叉树的结构最简单,规律性最强
可以证明,所有树都能转为唯一对应的二叉树,不失一般性
定义:是n(n≥0)个结点的有限集合,由一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成
二叉树性质
性质1: 在二叉树的第i层上至多有2i-1个结点(i>0)
性质2: 深度为k的二叉树至多有2k-1个结点(k>0)
性质3: 对于任何一棵二叉树,若2度的结点数有n2个,则叶子数(n0)必定为n2+1 (即n0=n2+1)

满二叉树:一棵深度为k 且有2k -1个结点的二叉树。
(特点:每层都“充满”了结点)
完全二叉树:深度为k 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k 的满二叉树中编号从1至n的结点一一对应。

性质4: 具有n个结点的完全二叉树的深度必为log2n+1
性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)
二叉树的存储结构
一、顺序存储结构
按二叉树的结点“自上而下、从左至右”编号,用一组连续的存储单元存储。
答:一律转为完全二叉树!
讨论:不是完全二叉树怎么办?
方法很简单,将各层空缺处统统补上“虚结点”,其内容为空
二、链式存储结构
二叉树结点数据类型定义:
typedef struct node *tree_pointer;
typedef struct node {
int data;
tree_pointer left_child, right_child;
} node;
树的三叉链表表示

2、遍历二叉树

树的性质确认

树的遍历引申

3、二叉树编程实践
typedef struct node{
int data;
struct node *lchild,*rchild;
} NODE;
NODE *root;

DLR(NODE *root )
{
if (root) //非空二叉树
{
printf(“%d”,root->data); //访问D
DLR(root->lchild); //递归遍历左子树
DLR(root->rchild); //递归遍历右子树
}
}

中序遍历算法
LDR(NODE *root)
{ if(root !=NULL)
{ LDR(root->lchild);
printf(“%d”,root->data);
LDR(root->rchild);
} }

后序遍历算法
LRD (NODE *root)
{if(root !=NULL)
{LRD(root->lchild);
LRD(root->rchild);
printf(“%d”,root->data);
} }
练习
例:编写递归算法,计算二叉树中叶子结点的数目
DLR_CountLeafNum(NODE *root)//采用中序遍历的递归算法
{
if ( root) //非空二叉树条件,还可写成if(root !=NULL )
{ if(!root->lchild&&!root->rchild) //是叶子结点则统计并打印
{ sum++; printf("%d\n",root->data); }
DLR_CountLeafNum(root->lchild); //递归遍历左子树,直到叶子处;
DLR_CountLeafNum(root->rchild);}//递归遍历右子树,直到叶子处;
} return(0);
}

前序遍历
思路:利用前序遍历来建树(结点值陆续从键盘输入,用DLR为宜)
Bintree createBTpre( )
{ Bintree T; char ch;
scanf(“%c”,&ch);
if(ch==’#’) T=NULL;
else
{ T=( Bintree )malloc(sizeof(BinTNode));
T->data=ch;
T->lchild=createBTpre();
T->rchild=createBTpre();
}
return T;
}
后序遍历销毁一个数
结论:通过中序遍历和先序遍历可以确定一个树
通过中序遍历和后续遍历可以确定一个数。
通过先序遍历和后序遍历确定不了一个数。

4、二叉线索树
概念
普通二叉树只能找到结点的左右孩子信息,而该结点的直接前驱和直接后继只能在遍历过程中获得。
若可将遍历后对应的有关前驱和后继预存起来,则从第一个结点开始就能很快“顺藤摸瓜”而遍历整个树了。

线索化过程就是在遍历过程(假设是中序遍历)中修改空指针的过程:
将空的lchild改为结点的直接前驱;
将空的rchild改为结点的直接后继。

二叉树线索化算法
void InTreading(BiThrTree p)
//中序遍历进行中序线索化
{
if §
{
InThreading(p->lchild); /左子树线索化/
if (!p->lchild) /前驱线索/
{ p->ltag=1; p->lchild=pre; }
if (!pre->rchild) /后继线索/
{ pre->rtag=1; pre->rchild=p; }
pre=p;
InThreading(p->rchild); /右子树线索化/
}
}
二叉树线索化遍历算法
程序注解 (非递归,且不用栈):
P=T->lchild; //从头结点进入到根结点;
while( p!=T)
{ while(p->LTaglink)p=p->lchild; //先找到中序遍历起点
if(!visit(p->data)) return ERROR; //若起点值为空则出错告警
while(p->RTag
Thread ……){ p=p->rchild; Visit(p->data);}
//若有后继标志,则直接提取p->rchild中线索并访问后继结点;
p=p->rchild; //当前结点右域不空或已经找好了后继,则一律从结点的右子树开始重复{ }的全部过程。
}
Return OK;

5、霍夫曼树
对于文本”BADCADFEED”的传输而言,因为重复出现的只有
”ABCDEF”这6个字符,因此可以用下面的方式编码:

接收方可以根据每3个bit进行一次字符解码的方式还原文本信息。
这样的编码方式需要30个bit位才能表示10个字符
那么当传输一篇500个字符的情报时,需要15000个bit位
在战争年代,这种编码方式对于情报的发送和接受是很低效且容易出错的。
如何提高收发效率?
要提高效率,必然要从编码方式的改进入手,要避免每个字符都占用相同的bit位

准则:任一字符的编码都不是另一个字符编码的前缀!
霍夫曼树
1.给定n个数值{ v1, v2, …, vn}
2.根据这n个数值构造二叉树集合F
F = { T1, T2, …, Tn}
Ti的数据域为vi,左右子树为空
3.在F中选取两棵根结点的值最小的树作为左右子树构造一棵新的二叉树,这棵二叉树的根结点中的值为左右子树根结点中的值之和
4.在F中删除这两棵子树,并将构造的新二叉树加入F中
5.重复3和4,直到F中只剩下一个树为止。这棵树即霍夫曼树
假设经过统计ABCDEF在需要传输的报文中出现的概率如下

霍夫曼树是一种特殊的二叉树
霍夫曼树应用于信息编码和数据压缩领域
霍夫曼树是现代压缩算法的基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值