二叉树
基本概念
二叉树是n个节点的有限集合
每个节点之多有两个子树
左右子树不能颠倒,有序树
满二叉树:一个高度为h,含有2^h-1个节点的二叉树
平衡二叉树:任意节点的左子树和右子树的深度之差不超过1
二叉树存储结构
顺序存储
#define MAXSIZE 100
struct TreeNode{
ElemType value;
bool isEmpty;
};
TreeNode t[MAXSIZE];
for(int i = 0;i<MAXSIZE;i++){
t[i].inRmpty = true;
}
- i的左孩子 ——2i
- i的右孩子 ——2i+1
- i的父节点 ——i/2
- i所在的层
二叉树的顺序存储结构只适合存储完全二叉树
链式存储
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
BiTree root = NULL;
root = new BiTNode;
root->data = 1;
root->lchild = NULL;
root->rchild = NULL;
二叉树的遍历
先序遍历
根左右
代码:
void PreOrder(BiTree T){
if(T!=NULL){
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
中序遍历
左根右
void PreOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
visit(T);
PreOrder(T->rchild);
}
}
后序遍历
左右根
void PreOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
PreOrder(T->rchild);
visit(T);
}
}
遍历算法的举例
求树的深度
int treeDepth(Bitree T){
if(T == NULL){
return 0;
}else{
int l = treeDepth(T->lchild);
int r = treeDepth(T->rchild);
return l>r?l+1:r+1;
}
}
二叉树的层序遍历
层序遍历结果为
abcdefghijkl
算法思想:
初始化一个辅助队列
根节点入队
若队列非空,则队头节点出队,访问该节点,并将其左右孩子插入队尾
重复3直到队空
void LevelOrder(Bitree T){
LinkQueue Q;
InitQueue(Q);
BiTree p;
EnQueue(Q,p);
while(!IsEmpty(Q)){
DeQueue(Q,p);
visit(p);
if(p->lchild!=NULL)
EnQueue(Q,p->lchild);
if(p->rchild!=NULL)
EnQueue(Q,p->rchild);
}
}
由遍历序列构造二叉树
若只给出一棵二叉树的前中后层遍历序列中的一种,不能唯一确定一个二叉树
线索二叉树
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadOde,*ThreadTree;//tag==0指向孩子,tag==1指向线索
二叉树线索化
用土方法找到中序前驱
//中序遍历
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
//访问节点q
void visit(BiTNode *q){
if(q==p)
final = pre;
else
pre = q;
}
BiTNode *p;
BiTNode *pre=NULL;
BiTNode *final=NULL;
中序线索化
ThreadNode *pre = NULL;
void CreatInThread(ThreadTree T){
pre = NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL)
pre->rtag = 1;
}
}
Typedef struct ThreadNode{
ElemType data;
mstruct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
void InThread(TreadTree T){
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild==NULL){
q->lchild=pre;
q->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=q;
pre->rtag=1;
}
pre=q;
}
线索二叉树寻找前驱后继
中序线索
//找到以p为根的子树中,第一个被中序遍历的结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag == 0)
p=p->lchild;
return p;
}
//在中序线索二叉树中找到结点p的后继节点
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag==0)
return Firstnode(p->rchild);
else return p->rchild;
}
//对中序二叉树进行中序遍历(线索二叉树实现非递归算法)
void Inorder(ThreadNode *p){
for(ThreadNode *p = Firstnode(T);p!=NULL;p=Nextnode(p))
visit(p);
}
寻找前驱原理相似
先序线索
先序线索二叉树第一个结点为根节点,即直接寻找后继结点
//在先序线索二叉树中找到结点p的后继节点
ThreadNode *NextNode(ThreadNode *p){
if(p->ltag = 0)
return p->lchild;
else return p->rchild;
}
树:存储结构
双亲表示法
每个节点保存指向双亲的指针
#define MAX_TREE_SIZE 100
typedef struct{
ElemType data;
int parent;
}PTNode;
typedef struct{
PTNode nodes[MAX_TREE_SIZE];
int n;
}PTree;
孩子表示法
顺序存储各个节点,每个节点中保存孩子链表头指针
孩子兄弟表示法(链式存储)
typedef struct CSNode{
ElemType data;//数据域
struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针
}CSNode,*CSTree;
森林和二叉树的相互转换
森林是m个互不相交的树的集合
各个树的根节点视为兄弟关系
树,森林的遍历
先根遍历。若树非空,先访问根节点,再依次对每棵子树进行先跟遍历
void PreOrder(TreeNode *R){
if(R!=NULL){
visit(R);
while()
PreOrder(T);
}
}
后根遍历。若树非空,先访问子节点,再依次对每棵子树进行后跟遍历
。。。
层次遍历
哈夫曼树
带权路径长度
结点的权:有某种含义的数值
结点的带权路径长度:从树的根到该结点的路径长度与权值的乘积
树的带权路径长度:树中所有叶节点的带权路径长度之和
在含有n个带权叶结点的二叉树中,其中带权路径长度最小的二叉树称谓哈夫曼树,最优二叉树[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QNsklAj-1683633433512)(1683632615035.png)]
哈夫曼编码
前缀编码
构造哈夫曼树的方法构造编码
并查集(2022新增考点)
{
visit®;
while()
PreOrder(T);
}
}
后根遍历。若树非空,先访问子节点,再依次对每棵子树进行后跟遍历
。。。
层次遍历
# 哈夫曼树
## 带权路径长度
结点的权:有某种含义的数值
结点的带权路径长度:从树的根到该结点的路径长度与权值的乘积
树的带权路径长度:树中所有叶节点的带权路径长度之和
在含有n个带权叶结点的二叉树中,其中带权路径长度最小的二叉树称谓哈夫曼树,最优二叉树
![请添加图片描述](https://img-blog.csdnimg.cn/c624c5a7aef146a2b5dfe5b49048b734.png)
**哈夫曼编码**
前缀编码
构造哈夫曼树的方法构造编码
# 并查集(2022新增考点)