基本的概念
只陈述一些我觉得平时见的比较少的概念,考试大概率不怎么考概念
括号表示法创建二叉树:遇到左括号,那后面的就是左孩子,遇到右括号,后面的就是右孩子,在同一个括号内的字母是同一层的树。
树的深度:树中节点的最大层次称为树的深度或高度
二叉树的定义
二叉树(Binary Tree)是n(n≥0)个节点所构成的集合,它或为空树(n = 0),或为非空 树。对于非空树T:
(1)有且仅有一个称之为根的节点;
(2)除根节点以外的其余节点分为两个互不相交的子集T1和T2,分别称为T的左子树和右子 树,且T1和T2本身又都是二叉树。
二叉树的性质
在二叉树的第i层上至多有2i−1 (i≥1)个节点
利用二叉链表存储树时,右指针指向兄弟结点,因为根节点没有兄弟结点,故根节点的右指针指向空。
深度为k的二叉树至多有2k−1(k≥1)个节点
对任何一棵二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0 = n2 + 1
满二叉树:深度为k且含有2k −1个节点的二叉树。每一层上的节点数都是最大节点数
完全二叉树:深度为k的、有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉 树中编号从1至n的节点一一对应时,称之为完全二叉树
度为0的节点的数量等于总度数减去有度数节点的数量
具有n个节点的完全二叉树的深度为 (log2n)向下取整 + 1①
如果对一棵有n个节点的完全二叉树(其深度为 (log2n)向下取整 + 1)的节点按层序编号(从 第1层到第 (log2n)向下取整 + 1层,每层从左到右),则对任一节点i(1≤i≤n),以下结论成立。
(1)如果i = 1,则节点i是二叉树的根,无双亲;如果i>1,则其双亲PARENT(i)是节点i/2(向下取整)。
(2)如果2i>n,则节点i无左孩子(节点i为叶子节点);否则其左孩子LCHILD(i)是节点2i。
(3)如果2i + 1>n,则节点i无右孩子;否则其右孩子RCHILD(i)是节点2i + 1。
二叉树的存储
//- - - - -二叉树的二叉链表存储表示- - - - -
typedef struct BiTNode{
TElemType data;//节点数据域
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree;
二叉树的遍历
//中序遍历
void InOrderTraverse(BiTree T)
{//中序遍历二叉树T的递归算法
if(T) //若二叉树非空
{
InOrderTraverse(T->lchild); //中序遍历左子树
cout<<T->data; //访问根节点
InOrderTraverse(T->rchild); //中序遍历右子树
}
}
//非递归的方法实现中序遍历
void InOrderTraverse(BiTree T)
{//中序遍历二叉树T的非递归算法
InitStack(S);p=T;
q=new BiTNode;
while(p||!StackEmpty(S))
{
if(p) //p非空
{
Push(S,p); //根指针进栈
p=p->lchild; //根指针进栈,遍历左子树
}
else //p为空
{
Pop(S,q); //退栈
cout<<q->data; //访问根节点
p=q->rchild; //遍历右子树
}
} // while
}
//先序遍历
void PreOrderTraverse(BiTree T)
{
if(T)
{
cout<<T->data;
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
//后序遍历
void PostOrderTraverse(BiTree T)
{
if(T)
{
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout<<T->data;
}
}
可以根据先序遍历和中序遍历,或者后序遍历和中序遍历来确定一棵二叉树,先序遍历或者后序遍历来确定根节点,然后再根据中序遍历来确定左右子树的位置,然后这样循环使用,再确定根节点。
二叉树遍历算法的运用
创建二叉树
void CreateBiTree(BiTree &T)
{//按先序次序输入二叉树中节点的值(单字符),创建二叉链表表示的二叉树T
cin>>ch;
if(ch=='#') T=NULL; //递归结束,建空树
else //递归创建二叉树
{
T=new BiTNode; //生成根节点
T->data=ch; //根节点数据域置为ch
CreateBiTree(T->lchild); //递归创建左子树
CreateBiTree(T->rchild); //递归创建右子树
} //else
}
复制二叉树
void Copy(BiTree T,BiTree &NewT)
{//复制一棵和T完全相同的二叉树
if(T==NULL) //如果是空树,递归结束
{
NewT=NULL;
return;
}
else
{
NewT=new BiTNode;
NewT->data=T->data; //复制根节点
Copy(T->lchild,NewT->lchild); //递归复制左子树
Copy(T->rchild,NewT->rchild); //递归复制右子树
} //else
}
计算二叉树的深度
int Depth(BiTree T)
{//计算二叉树T的深度
if(T==NULL) return 0; //如果是空树,深度为0,递归结束
else
{
m=Depth(T->lchild); //递归计算左子树的深度记为m
n=Depth(T->rchild); //递归计算右子树的深度记为n
if(m>n) return(m+1); //二叉树的深度为m与n的较大者加1
else return(n+1);
}
}
线索二叉树
n个节点必然存在n+1个空链域,因此要充分利用这些空链域
若节点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱; 若节点有右子树,则其rchild域指示其右孩子,否则令rchild域指示其后继。为了避免混淆,尚需改 变节点结构,增加两个标志域
以节点p为根的子树中序线索化
void InThreading(BiThrTree p)
{//pre是全局变量,初始化时其右孩子指针为空,便于在树的最左点开始建线索
if(p)
{
InThreading(p->lchild); //左子树递归线索化
if(!p->lchild) //p的左孩子为空
{
p->LTag=1; //给p加上左线索
p->lchild=pre; //p的左孩子指针指向pre(前驱)
} //if
else p->LTag=0;
if(!pre->rchild) //pre的右孩子为空
{
pre->RTag=1; //给pre加上右线索
pre->rchild=p; //pre的右孩子指针指向p(后继)
} //if
else pre->RTag=0;
pre=p; //保持pre指向p
InThreading(p->rchild); //右子树递归线索化
}
}
① 如果p非空,左子树递归线索化。
② 如果p的左孩子为空,则给p加上左线索,将其LTag置为1,让p的左孩子 指针指向pre(前驱);否则将p的LTag置为0。
③ 如果pre的右孩子为空,则给pre加上右线索,将其RTag置为1,让pre的右 孩子指针指向p(后继);否则将pre的RTag置为0。
④ 将pre指向刚访问过的节点p,即pre = p。
⑤ 右子树递归线索化
(1)在中序线索二叉树中查找
① 查找p指针所指节点的前驱:
若p->LTag为1,则p的左链指示其前驱;
若p->LTag为0,则说明p有左子树,节点的前驱是遍历左子树时最后访问的一个节点 (左子树中最右下的节点)。
② 查找p指针所指节点的后继: l 若p->RTag为1,则p的右链指示其后继;
若p->RTag为0,则说明p有右子树。根据中序遍历的规律可知,节点的后继应是遍历其 右子树时访问的第一个节点,即右子树中最左下的节点。例如在找节点*的后继时,首先沿右指针 找到其右子树的根节点-,然后顺其左指针往下直至其左标志为1的节点为节点*的后继,
(2)在先序线索二叉树中查找
① 查找p指针所指节点的前驱:
若p->LTag为1,则p的左链指示其前驱;
若p->LTag为0,则说明p有左子树。此时p的前驱有两种情况:若*p是其双亲的左孩子, 则其前驱为其双亲节点;否则应是其双亲的左子树上先序遍历最后访问到的节点。
② 查找p指针所指节点的后继:
若p->RTag为1,则p的右链指示其后继;
若p->RTag为0,则说明p有右子树。按先序遍历的规则可知,*p的后继必为其左子树根 (若存在)或右子树根。
(3)在后序线索二叉树中查找
① 查找p指针所指节点的前驱:
若p->LTag为1,则p的左链指示其前驱;
若p->LTag为0,当p->RTag也为0时,则p的右链指示其前驱;若p->LTag为0,而p-> RTag为1时,则p的左链指示其前驱。
② 查找p指针所指节点的后继情况比较复杂,分以下情况讨论:
若*p是二叉树的根,则其后继为空;
若*p是其双亲的右孩子,则其后继为双亲节点;
若*p是其双亲的左孩子,且*p没有右兄弟,则其后继为双亲节点;
若*p是其双亲的左孩子,且*p有右兄弟,则其后继为双亲的右子树上按后序遍历列出的 第一个节点(右子树中最左下的叶节点)。
树和森林
哈夫曼树
哈夫曼树中没有度为一的结点
【数据结构——五分钟搞定哈夫曼树,会求WPL值,不会你打我】 数据结构——五分钟搞定哈夫曼树,会求WPL值,不会你打我_哔哩哔哩_bilibili
注意一个比较特殊的哈夫曼树ht存储结构