概念:
1.在二叉树的第i层上至多有2^(i-1)个结点。
2.深度为k的二叉树至多有2^k - 1个结点。
3.对任意一棵二叉树T, 若终端结点数为N0, 度为2的结点数为N2, 则N0 = N2 + 1;
4.具有n个结点的完全二叉树的深度为|log2(n)|+1。
满二叉树:
深度为k且含有2^k - 1个结点的二叉树称为满二叉树。
满二叉树结点的连续编号:
对含有n个结点的满二叉树,约定从根开始,按根从上到下,每层内从左到右,逐个对每一结点进行编号1,2,……,n。
完全二叉树:
深度为k,结点数为n的二叉树,当且仅当其n个结点与满二叉树中连续编号为1至n的结点位置一一对应时,称为完全二叉树。两个特征:(1)所有叶子结点只可能出现在层号最大的两层上。(2)对任意结点,若右子树的层高为k,则其左子树的层高只可为k或k+1。
先序递归遍历:
void PreOrder(BiTree root)
{
if (root
) {
printf ("%c", root
->
data
);
PreOrder (root -> Lchild
);
PreOrder (root -> Rchild
);
}
}
中序递归遍历:
void InOrder (
BiTree root
)
{
if (root
) {
InOrder (root -> Lchild
);
printf ("%c", root -> data);
InOrder (root -> Rc
hild
);
}
}
后序递归遍历:
void PostOrder (BiTree root
)
{
if (root
) {
PostOrder
(root -> Lchild
);
PostOrder (root -> Rchild
);
printf ("%c
", root -> data
);
}
}
先序非递归遍历:
void PreOrder (BiTree root)
void PreOrder (BiTree root)
{
SeqStack *S;
BiTree p;
InitStack
(S); p = root;
while (p
!= NULL || IsEmpty(S
)
) {
while (p != NULL
) {
printf ("%c", p -> data
);
Push(S, p
);
p = p -> Lchild;
}
if (!IsEmpty (S
)) {
Pop (S, &p);
p = p -> Rchild;
}
}
}
中序非递归遍历:
void PreOrder (BiTree root)
{
SeqStack *S;
BiTree p;
InitStack
(S); p = root;
while (p
!= NULL || IsEmpty(S
)
) {
while (p != NULL
) {
Push(S, p
);
p = p -> Lchild;
}
if (!IsEmpty (S
)) {
Pop (S, &p); printf ("%c", p->data);
p = p -> Rchild;
}
}
}
后序非递归遍历二叉树
void PostOrder(BiTree root
)
{
SeqStack *S;
BiTree p, q;
InitStack(S
); p = root; q = NULL;
while (p != NULL || IsEmpty(S
)
) {
while (p != NULL
) {
Push(S, p
); p = p -> Lchild;
}
if (!
Is
Empty (S
)
) {
Top (S, &p
);
if (p
-> Rchild == NULL || p -> Rchild == q
) {
Pop(S, &p); printf ("%c", p->data
); q= p; p = NULL;
}
else {
p = p -> Rchild;
}
}
}
}
二叉树的层次遍历
void LevelOrder (BiTree root
)
{
SeqQueue *Q;
BiTree *p;
InitQueue (Q
);
EnterQueue(Q, root
);
while ( !IsEmptyQueue(Q
)
) {
DeleteQueue(Q, &p); printf ("%c
", p -> data
);
if (p -> Lchild !
= NULL
)
EnterQueue(Q, p->Lchild
);
if (p -> Rchild != NULL
)
EnterQueue(Q, p ->Rchild
);
}
}
//先序遍历统计二叉树中结点数
void PreOrder
(BiTree root
)
{
if (root
) {
count ++;
PreOrder (root -> Lchild
);
PreOrder (root -> Rchild
);
}
}
//中序遍历输出二叉树的叶子结点数
void InOrder (BiTree root)
{
if (root) {
InOrder (root -> Lchild);
if (root -> Lchild == NULL || root -> Rchild == NULL)
printf (root -> data);
InOrder (root -> Rchild);
}
}
后序遍历统计叶子结点数
int leaf (BiTree root
)
{
int n1, n2;
if (root == NULL
)
return 0;
if (root -> Lchild == NULL && root -> Rchild == NULL)
return 1;
n1 = leaf (root -> Lchild
);
n2 = leaf (root -> Rchild
);
return (
n1 + n2)
;
}
全局变量法求二叉树的高度
void TreeDepth(BiTree root, int h
)
/*h为结点所在层次,初始值为1;depth为全局变量,初始值为0*/
{
if (root) {
if (h > depth) depth = h;
TreeDepth(root -> Lchild
,
h+1);
TreeDepth(root -> Rhild,
h+1
);
}
}
求二叉树高度
int PostTreeDepth(BiTree root
)
{
int h1, h2;
if (root == NULL
) return 0;
else {
h1 = PostTreeDepth(root -> Lchild
);
h2 = PostTreeDepth(root -> Rchild
);
h = (h1 > h2 ? h1 : h2
) + 1;
return h;
}
}
求结点的双亲
BiTree parent (BiTree root, BiTree current)
{
BiTree *p;
if (root == NULL) return NULL;
if (root -> lchild == current || root -> rchild == current
)
return root;
p = parent(root -> lchild, current
);
if (p != NULL
) return p;
else return (parent(root -
> rchild, current
)
);
}
二叉树相似性判定
int like(BiTree t1, BiTree t2
)
{
int like1, like2;
if (t1 == NULL && t2 == NULL
) return 1;
else if (t1 == NULL || t2 ==NULL
) return 0;
else {
like1 = like (t1 -> Lchild, t2 -> L
child);
like2 = like (t1 -> Rchild
, t2 -> Rchild
);
return (like1 && like2
);
}
}
由遍历序列确定二叉树
1.由先序中序确定二叉树
由先序序列第一个结点确定根D;
由根节点D分割中序序列,左为左子树L
,右为右子树R
;
根据左子树L的结点个数,分割先序序列;
类推。
2.由中序后序确定二叉树
由后序最后一个结点确定根节点D;
由根节点D分割中序序列;
根据左子树L的结点个数,分割后序序列;
类推。
先序和后序不可以确定二叉树,若为单叉树,所有结点最大长度为1,则不能确定。
线索二叉树
结构特点:
若结点有左子树,则Lchild域仍指向其左孩子;否则,Lchild域指向其某中遍历序列中的直接前驱结点。
若结点有右子树,则Rchild域仍指向其右孩子;否则,Rchild域指向其某种遍历序列中的直接后继结点。
为避免混淆,增设两个标志域:Ltag
和 Rtag. 其为0时,child域指向左右孩子,其为1时,child域指向结点的遍历前驱或后继。
二叉树的中序线索化算法
void Inthread (BiTree root
)
{
if (root != NULL
) {
Inthread (root -> Lchild
);
if (root -> Lchild == NULL
) {
root -> Lchild = pre; root -> Ltag = 1;
}
if (pre != NULL && pre -> Rchild == NULL
) {
pre->Rchild = root; pre -> Rtag = 1;
}
pre = root;
Inthread(root -> Rchild
);
}
}
森林,树,二叉树转换
1.树转换为二叉树
加线--
树中所有相邻兄弟之间加一条线
删线--
对每个结点,只保留与第一个孩子的线
旋转调整
2.森林转换为二叉树
转换--将森林中每棵树转换为二叉树
加线--将相邻各棵二叉树的根节点之间加线
旋转调整
3.二叉树转换为森林
加线--若某结点是其双亲的左孩子,则把该结点的右孩子,右孩子的右孩子、
……都与该结点的双亲结点间连线。
删线--删除原二叉树中所有双亲与孩子的连线。
旋转调整
哈夫曼树基本概念:
树的路径长度:从树根到每个结点的路径长度之和称为树的路径长度。完全二叉树是结点给定情况下路径长度最短的二叉树。
带权路径长度:结点到树根间的路径长度与结点的权的乘积,称为该结点的带权路径长度。树的带权路径长度为树中所有叶子带权路径长度和。在叶子结点和权值确定情况下,和最小的为最优二叉树即哈夫曼树。
树的路径长度:从树根到每个结点的路径长度之和称为树的路径长度。完全二叉树是结点给定情况下路径长度最短的二叉树。
带权路径长度:结点到树根间的路径长度与结点的权的乘积,称为该结点的带权路径长度。树的带权路径长度为树中所有叶子带权路径长度和。在叶子结点和权值确定情况下,和最小的为最优二叉树即哈夫曼树。
哈夫曼树中没有度为1的结点,这类二叉树称为正则二叉树,n个叶子的二叉树,恰有n-1个度为2的结点,所有哈夫曼树恰有2n-1个结点。
哈夫曼编码是前缀码:
由于每个字符的哈夫曼编码是从根到相应叶子的路径上分支符号组成的串,字符不同,相应非叶子也不同,两条路径前半部分可能相同但最后一定会分叉。所以一条路径不可能是另一条路径的前缀。