一、树的概念与性质
结点的度:结点拥有的子树的个数
树的度:所有结点度的最大值
子孙结点:每个结点的所有子树的结点
结点的层次:
树的高度:所有结点的层次的最大值
有序树:是指从左到右都是有序的
性质1:结点总数等于所有结点的度之和+1.
性质2:m叉树上,第i层最多有m^(i-1)个结点。(根节点层次为1)
二、二叉树
1.基本性质
非空二叉树,第i层最有2^(i-1)个结点。
高度为h的二叉树结点总数最多为2^h-1。→满二叉树:高度为h且有2^h-1个结点。
叶子结点树为n0,度为2的结点树为n2,则n0=n2+1。
→若推广到i叉树,则n0=n2+2n3+……+(i-1)ni+1
例1:若某二叉树有20个叶子结点,有30个结点仅有一个孩子,问该二叉树的总结点数。
答案是69。设有两个孩子的结点数为x,利用结点总数=结点的度+1求解出x,进一步算出。
例2:
完全二叉树:深度为h的二叉树,前h-1层是满的,并且第h层结点从左至右依次排列。
(满二叉树是底层数满的完全二叉树)
→特别性质:
1.度为1的结点仅有1个或0个(叶子节点尽可能往左排)
2.具有n个节点的完全二叉树的深度为 └log2n┘+ 1
3.叶子结点个数:
当n1=0时(即度为1的节点为0个时,此时n为奇数)或者n为奇数时
n0= (n+1)/2;
当n1=1时(即度为1的节点为1个时,此时n为偶数)或者n为偶数
n0= n/2;
例题:具有100个结点的完全二叉树,问其叶子结点数。
答案是50。
第i层至多有2^(i-1)个结点,若是满二叉树则有1+2+4+...2^(i-1)个结点即(2^i)-1个结点。当i=7时最接近101,所以是7层。故第7层有64-(128-101)=37个结点即第7层有37个叶子。而第6层有32个结点,故第6层有32-(37/2的最大整数)=13个叶子节点。所以总共50个叶子结点。
2.存储结构
顺序存储:
链式存储:
(1)二叉链表结构(常考、递归必用):
结点结构中包含两个指针域分别指向左、右孩子。设有n个结点则有2n个指针域,其中只有n-1个指针域不为空(除根结点外都有指针指向结点)
(2)三叉链表结构:增加一个指针域包含双亲地址,方便向上检索。
3.操作算法
注意牢牢抓住递归思想
遍历(必考,重点)
先序遍历:根到左到右
中序遍历:左到根到右
后序遍历:左到右到根
(总是从左到右,只是遍历根的位置不同(在前/在中/在后))
注意考虑在每个分支的二叉树也仍然遵循这样的先后次序!
层次遍历:从根节点从上到下逐层遍历,同一层从左到右依次遍历
·二叉树有唯一确定的遍历序列,而一个遍历序列并不能确定二叉树结构。
例题:(判断)带空指针的前序遍历序列可以唯一确定一颗树。(√)
例题:(前中后序列知二求一)
构造
由单个遍历序列构造是重点:
//带空指针标记的先序序列构造二叉树
template<class T>
BinNode<T>*BinTree<T>::CreateByPre(vector<T>&pre,int&i)
{
T e=pre[i];
i++;
if(e=='*')
return NULL;
BinNode<T>*p=new BinNode<T>;
p->data=e;
p->lchild=CreateByPre(pre,i);
p->rchild=CreateByPre(pre,i);
return p;
}
template<class T>
BinTree<T>::BinTree<T>(vector<T>&pre)
{
int i=0;
root=CreateByPre(pre,i);
}
拷贝构造:
析构:
计算二叉树的结点数(重要!)
template<class T>
int BiTree<T>::Count(BiNode<T>*p)
{
if(p==NULL)
return 0;
int left=Count(p->lchild);
int right=Count(p->rchild);
return 1+left+right;
}
template<class T>
int BiTree<T>::Count()
{
return Count(root);
}
计算二叉树的高度
根据关键值查找结点
查找该结点的双亲
例1:输入二叉树的带空指针的前序序列,建立一颗二叉树并求该树的叶子结点总数。
template<class T>
int BiTree<T>::TreeLeaf_Count(BiNode<T>*p)
{
if(p==NULL)
return 0;
if (p->left == NULL && p->right == NULL) //说明根结点就是叶子结点
return 1;
int left=TreeLeaf_Count(p->lchild);
int right=TreeLeaf_Count(p->rchild);
return left+right;
}
template<class T>
int BiTree<T>::TreeLeaf_Count()
{
return TreeLeaf_Count(root);
}
→那求度为2的结点总数呢?
template<class T>
int BiTree<T>::Count2(BiNode<T>*p)
{
if(p==NULL)
return 0;
else
{
int left=Count2(p->lchild);
int right=Count2(p->rchild);
if (p->left != NULL && p->right != NULL) //判断根节点的度数是否为2
return 1+left+right;
else
return left+right;
}
}
template<class T>
int BiTree<T>::Count2()
{
return Count2(root);
}
→求第k层结点数目
(以下都为核心代码即
if(p==NULL)
return 0;
else
{
...........
}里的内容)
if (k == 1)
return 1;
k--;
return KLevel_Count(p->lchild, k) + KLevel_Count(p->rchild, k);
→那求单分支的结点个数?
if ((p->lchild==NULL && p->rchild!=NULL) || (p->lchild!=NULL && p->rchild==NULL)
return Single_Count(p->lchild)+ Single_Count(p->rchild)+1;//根结点就是单分支结点
else
return Single_Count(b->lchild)+ Single_Count(b->rchild);
→求所有双分支结点的个数?
if(p->lchild!=NULL && p->rchild!=NULL)
return Double_Count(p->lchild)+Double_Count(p->rchild)+1;//根节点就是双分支
else
return Double_Count(p->lchild)+Double_Count(p->rchild);
}
→二叉树中结点值等于某给定值结点个数。
template<class T>
int BiTree<T>::CountNode(BiNode<T> *p,T x)
{
if(p != NULL)
{
if(p->data == x)
return CountNode(p->lchild,x)+CountNode(p->rchild,x)+1;
else
return CountNode(p->lchild,x)+CountNode(p->rchild,x);
}
return 0;
}
例2: 设采用二叉链表存储二叉树,试编写算法在二叉树中查找data域为x的结点。
例3:设采用二叉链表存储二叉排序树,试编写算法在二叉排序树中求任意两个不同结点的共同祖先。
三、线索二叉树
1.基本概念:
(为了遍历中快速查找结点的前驱和后继)
二叉树有n个结点,则总的有2n个指针,有指向的有n-1个指针(根节点无指向)(或者说非空链域),则无指向的空指针有n+1个。
为利用这些空指针域,引入线索(指向前驱或后继的指针)。结点中空的左孩子指针域lchild存放前驱地址,空的rchild指向后继地址。
例1:判断:线索二叉树中,任一结点均有指向前驱和后继的线索。(×)得是空指针。
2.存储结构:
0指向孩子指针,1指向前驱或后继
3.操作算法:
例1:用非递归的方法实现中序遍历中序线索树。
template<class T>
void InBiThrTree<T>::Traverse()
{
BiThrNode<T>*p=root;
while(p=>ltype==LINK)//1.找到中序遍历的起点,即最左下方的点
p=p->lchild;
while(p!=NULL)
{
cout<<p->data<<" ";//2.访问当前你借点
p=GetNext(p);//3.找到当前结点的后继结点
}
}
四、树
1.存储结构
例1:树的存储方法有哪些?任画一个树,举例说明具体存储结构。
![](https://i-blog.csdnimg.cn/blog_migrate/a1cc79affe239f9409cb3ff61bda0fbc.jpeg)
2.操作算法
构造:
依据序偶像结点添加结点和关系
计算树的高度:
计算所有结点的度:
例1:
遍历:
前根遍历:访问根结点,从左到右前根遍历每棵子树。
后根遍历:从左到右根后遍历每棵子树,再访问根结点。
当多叉树采用孩子兄弟表示法存储为二叉链表结构时,可借助二叉链表的遍历算法实现多叉树的遍历算法。在这样的多叉树(逻辑结构)和二叉树(存储结构)的对应关系时,树的前根遍历等效于二叉树的先序遍历,树的后根遍历是先访问左子树(即树的所有子树)再访问根结点,等效于二叉树的中序遍历。
五、Huffman树
基本概念:
无度为1的结点,n=n1+n2+n0=2n0-1
小的往下沉,大的靠近根节点,注意大小别比错了!
编码:左为0右为1
存储结构:
采用三叉链表结构
struct HuffmanNode
{
char data;
double weight;
int parent,lchild,rchild;//孩子结点的位置
}
操作算法:
构造函数:
编码、译码