数据结构——树和二叉树

数据结构——树和二叉树

边学习边整理,有什么不对的地方还请大家多指正~

1.树的定义

是n(n>=0)个结点的有限集
在任意一棵非空树中:(1)有且仅有一个根结点
(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集,每个集合又是一棵树,称为根的子树。

二叉树是n(n>=0)个结点的有限集

  • 若n=0,则称为空二叉树;
  • 若n>0,则:
    有1个特定的称为的结点
    由两棵互不相交的左右子树组成,并且左右子树都是二叉树(递归定义)。
    *二叉树并不是树的特例

2.二叉树的性质

性质(1)二叉树的第i层上至多2i-1个结点(i>=1)
性质 (2) 深度为k的二叉树至多2k-1个结点(k>=1)
性质 (3)任何一棵二叉树,其叶结点数为n0,度为2的结点数为n2,则n0=n2+1
满二叉树: 一棵深度为k且有2k-1个结点的二叉树(即每层结点数都达到最大)
完全二叉树: 设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数
性质 (4) 具有n个结点的完全二叉树深度为[log2n]+1
性质(5)完全二叉树的编号规则

3.二叉树的存储结构

①顺序存储结构
定义:用一片地址连续的存储单元存储二叉树的结点
缺点:仅适合存储完全二叉树,对一般的二叉树造成空间浪费,因为二叉树本身为非线性结构,顺序存储则将它线性编号存到一维数组中,所以对二叉树一般用链式存储
②链式存储结构

// 二叉链表存储表示
typedef struct BiTNode{
TElemType data;
Struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;

二叉链表的特点:1.一个二叉链表由根指针唯一确定
2.具有n个结点的二叉链表共有2n个指针域,其中n-1个指向左右孩子,n+1个为空链域

// 三叉链表存储表示
typedef struct TriBTNode{
TElemType data;
Struct TriBTNode *lchild,*rchild,*parent;
}TriBTNode,*TriBTree;

4.二叉树的遍历及其应用

定义:遍历就是按某种次序访问二叉树的结点使每个结点被访问一次且仅此一次。
四种遍历方法:a.先序遍历 b.中序遍历 c.后序遍历 d.层序遍历

// 先序遍历的递归算法
void PreOrder(BiTree T){
if(T==NULL) return;
else{
printf(T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}

可类似实现中序遍历和后序遍历的递归算法

// 二叉链表层序遍历算法
void LevelOrder(BiTree T){
InitQueue(Q);
if(T){
EnQueue(Q,T);
while(!(QueueEmpty(Q)){
DeQueue(Q,b);
printf(b->data);
if(b->lchild) EnQueue(Q,b->lchild);
if(b->rchild) EnQueue(Q,b->rchild);
}}}

遍历的应用: ①表达式的表示
②二叉树的建立
1.先序建立

//二叉树先序建立的递归算法
Status CreateBiTree(BiTree &T){
//按先序次序输入二叉树中结点的值(一个字符),空格字符表示空树
//构造二叉链表表示的二叉树T
scanf("&ch");
if(ch == ' ') T=NULL;
else{
if(!(T=(BiTNode*)malloc(sizeof(BiTNode)))) exit(OVERFLOW);
T->data =ch;
CreatBiTree(T->lchild);
CreatBiTree(T->rchild)}
return Ok;
}//CreatBiTree

2.根据两个序列建立
先序和后序不能确定唯一的一棵树
必须有中序才行
③计算树的深度

//二叉树深度计算的递归算法
int TreeDepth(BiTree T){
if(T==NULL) h=0;
else{
hl=TreeDepth(T->lchild);
hr=TreeDepth(T->rchild);
h=hl>hr? hl+1 : hr+1;
}
return h;
}

统计二叉树中结点数目也可以递归的方法

5.Huffman树(最优二叉树)

①如何构建
②Huffman树的特点:
a.没有度为1 的结点
b.n个叶结点的Huffman树共有n+1个结点
c.huffman树不唯一,但WPL值唯一
③Huffman树的应用
Huffman编码
优点:不等长,压缩,无损编码
缺点:无错误保护功能,扫描两次
Huffman译码
构造Huffman树

//存储结构
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
//构造huffman树
void CreatHuffmanTree(HuffmanTree &HT,int W[],int n[]){
if(n<=1) return ;
m=2*n-1;
HT=(HUffmanTree)malloc((m+1)*sizeof(HTNode));
for(i=1;i<=m;i++)
if(i<=n) HT[i]={w[i],0,0,0};
else HT[i]={0,0,0};
for(i=n+1;i<=m;i++){
Select(Ht,i-1,s1,s2);
HT[s1].parent=i; HT[s2].parent=i;
Ht[i].lchild=s1; HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}

Huffman编码

//huffman编码算法
void HuffmanCoding(HuffmanTree HT,HuffmanCode &HC,int n){
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
cd=(char*)malloc(n*sizeof(char));
cd[n-1]='\0';
for(i=1;i<=n;i++){
start=n-1;
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
else cd[--start]='1'
HC[i]=(char*)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
free(cd);
}

6.树、森林和二叉树

①树和二叉树的转换
第一步:把所有兄弟连线
第二步:断开树和其它孩子的连线,只保留其和第一个孩子的连线
第三步:旋转树,使得其孩子靠左,兄弟靠右
②树的遍历
a.树的先根遍历
定义:若树为空,则空操作;否则访问树的根结点,再依次先根遍历每棵子树
树的先根遍历与对应二叉树的遍历有何关系?
答:和对应二叉树的先序遍历相对应
b.树的后根遍历
定义:若树为空,则空操作;否则依次后根遍历每棵子树,再访问根结点
树的后根遍历与对应二叉树的中序遍历
③森林和二叉树的转换
第一步:兄弟连线(每棵树的树根被认为是兄弟)
第二步:断开结点和其它孩子的连线,只保留其和第一个孩子的连线
第三步:旋转
④森林的遍历
a.森林的先序遍历
定义:若森林为空,则空操作;否则访问第一棵树的根结点,再先序遍历第一棵树的中根结点的子树森林,再先序遍历其它树
森林的先序遍历与对应二叉树的先序遍历相对应
b.森林的中序遍历
定义:若森林为空,则空操作;否则中序遍历第一棵树根结点的子树森林,访问第一颗树根结点,,再中序遍历其它树
森林的中序遍历与对应二叉树的中序遍历相对应

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值