1 树的基本概念
1.1 树的形式定义
- D为树T中包含n个结点的有限集合,R为树中结点之间关系的集合。
- 当n=0时,树为空树;当n>0时,R是D上某个二元关系的集合,满足以下条件:
- 有且仅有一个结点,称为根结点,该结点没有直接前驱结点;
- 除根结点外,每个结点有且仅有一个前驱结点;
- D中每个结点可以有零个或多个后继结点;
1.2 树的递归定义
- 树是由n(n≥0)个结点组成的有限集T。
- 当n=0时,它是一个空树;当n>0时,它满足两个条件:
- 有且仅有一个特定的结点,称为根结点。
- 除根结点以外的其余结点分为m个(m≥0)互不相交的有限集T1、T2、……Tm,其中每个集合又都是一棵树,称T1、T2、……Tm为根结点的子树。
1.3 树的基本术语
- 结点:树的数据元素
- 结点的度:该结点的分支的个数
- 树的度:树中所有结点的度的最大值
- 结点的层次:从根到该结点的层数(根结点算第一层)
- 树的深度:所有结点的层次的最大值
- 根结点:在非空树中,无前驱结点的结点
- 分支结点:度不为0的结点
- 叶结点:度为0的结点
- 孩子结点:结点的子树的根
- 双亲结点:孩子结点的根结点
- 兄弟结点:具有共同双亲的结点
- 堂兄弟结点:双亲互为兄弟的结点
- 祖先结点:从根到该结点的所经历的所有结点
- 子孙结点:以某结点为根的子树中的任一结点
1.4 二叉树的递归定义
二叉树是结点的有限集合,这个有限集,或为空集,或由一个根结点及两棵互不相交的,分别叫作这个根的左子树和右子树的二叉树组成。
【注意】二叉树不是树的特殊情况。
1.5 存储方法
双亲表示法——求父结点方便
孩子表示法——求子结点方便
双亲孩子表示法—求父结点和子结点方便
二叉树表示法——把一个普通树转化成二叉树来存储
1.6 满二叉树VS完全二叉树
满二叉树
- 定义:深为
k
且有 2 k − 1 2^k-1 2k−1个结点的二叉树。- 编号:约定编号从根开始,自上而下,自左而右,给二叉树中的每个结点一个从1开始的连续的编号。
完全二叉树
- 定义:深为
k
且有n
个结点的二叉树,当且仅当其每一个结点都与深度为k
的满二叉树中编号从1至n的结点一一对应。
满二叉树是完全二叉树,反之则不一定!
2 二叉树的性质
- 在二叉树的第 i 层上至多有
2
i
−
1
2^{i-1}
2i−1个结点 (
i
≥
1
i\geq1
i≥1)
证明:
(1)i=1
时,只有一个根结点, 2 i − 1 2^{i-1} 2i−1 = 2 0 2^0 20= 1,结论正确;
(2)假设n=k-1
命题成立,即第k-1
层上至多有 2 k − 2 2^{k-2} 2k−2 个结点,则当n=k
时,每个结点至多有两棵子树;
则k
层结点最多为k-1
层的2倍,故 s < = 2 ∗ 2 k − 2 = 2 k − 1 s<=2*2^{k-2}=2^{k-1} s<=2∗2k−2=2k−1,第i
层至多有 2 i − 1 2^{i-1} 2i−1 个结点;
(3)由归纳法,即得证。
- 深度为 k 的二叉树至多有 2 k − 1 2^k-1 2k−1个节点( k ≥ 1 k\geq1 k≥1)
20+21+…+2k-1=2k-1(利用等比数列求和公式得到结果)
- 对任何一颗二叉树 T,如果其终端结点树为 n 0 n_0 n0,度为 2 的结点数为 n 2 n_2 n2,则 n 0 n_0 n0 = n 2 n_2 n2+1
证明:
终端结点数就是叶结点数了,而一颗二叉树,除了叶结点外,剩下的就是度为 1 和 2 的结点数了,我们设 n 1 n_1 n1 为度是 1 的结点数,则树 T 的总结点数为n = n 0 n_0 n0 + n 1 n_1 n1 + n 2 n_2 n2
再换一个角度,数一下二叉树中连接线的总数,由于根节点没有双亲,所以一个二叉树中,连接线数等于结点树-1, n 1 n_1 n1 的度为 1 所以它仅有一条连接线, n 2 n_2 n2同理,代数表达式就是
n − 1 n-1 n−1 = n 1 n_1 n1 + 2 n 2 2n_2 2n2
再结合等式
n = n 0 n_0 n0 + n 1 n_1 n1 + n 2 n_2 n2
推导出
n 0 n_0 n0 + n 1 n_1 n1 + n 2 n_2 n2-1 = n 1 n_1 n1 + 2 n 2 2n_2 2n2
所以:
n 0 n_0 n0 = n 2 n_2 n2 + 1
- 具有 n 个结点的完全二叉树的深度为 [ log 2 n ] [\log_2 n] [log2n] + 1 ,([x]代表不大于 x 的最大整数)
证明:
1)对于满二叉树,深度为 k 的满二叉树至多有 2 k − 1 2^k-1 2k−1个节点( k ≥ 1 k\geq1 k≥1)
那么由:n = 2 k − 1 n=2^k-1 n=2k−1
可以倒推
k = log 2 ( n + 1 ) k=\log_2(n+1) k=log2(n+1)
2)对于完全二叉树,它的结点数一定少于等于同样深度的满二叉树 2 k − 1 2^k-1 2k−1,但一定多于 2 i − 1 − 1 2^{i-1}-1 2i−1−1,即:
2 i − 1 − 1 < n ≤ 2 k − 1 2^{i-1}-1<n\leq2^k-1 2i−1−1<n≤2k−1
所以
2 i − 1 ≤ n < 2 k 2^{i-1}\leq n < 2^k 2i−1≤n<2k
两边取对数:
2 i − 1 ≤ n < 2 k 2^{i-1}\leq n < 2^k 2i−1≤n<2k
而 k 又是整数:
k = [ log 2 n ] + 1 k = [\log_2 n] + 1 k=[log2n]+1
- 对于一个有 n 个结点的完全二叉树(或满二叉树)的结点按层序顺序从左到右编号,对任意结点 i 有:
1)如果 i = 1,那么结点 i 为该树的根,无双亲;若 i > 1 ,则其双亲是结点 [i/2]
2)如果 2i > n,则结点无左孩子(结点 i 为叶子结点),否则其左孩子结点是 2i
3)如果 2i + 1 > n,则结点 i 无右孩子,否则其右孩子是结点 2i + 1
3 代码实现
创建二叉树:
#include <stdio.h>
#include <stdlib.h>
typedef char ElementType;
typedef struct Binary {
ElementType data;
struct Binary *lchild;
struct Binary *rchild;
} *BinaryTree;
/* Recursive implementation 1 */
BinaryTree CreateBinaryTree_1(void)
{
BinaryTree bt;
char ch;
scanf("%c", &ch);
if (ch == '#') {
bt = NULL;
} else {
bt = (BinaryTree)malloc(sizeof(struct Binary));
bt->data = ch;
bt->lchild = CreateBinaryTree_1();
bt->rchild = CreateBinaryTree_1();
}
return bt;
}
/* Recursive implementation 2 */
void CreateBinaryTree_2(BinaryTree *bt)
{
char ch;
scanf("%c", &ch);
if (ch == '#') {
*bt = NULL;
} else {
*bt = (BinaryTree)malloc(sizeof(struct Binary));
(*bt)->data = ch;
CreateBinaryTree_2(&((*bt)->lchild));
CreateBinaryTree_2(&((*bt)->rchild));
}
}
void PreviousOrderTraverse(BinaryTree T)
{
if (T == NULL) {
return;
}
printf("%c", T->data);
PreviousOrderTraverse(T->lchild);
PreviousOrderTraverse(T->rchild);
}
int main(void)
{
BinaryTree bt;
// bt = CreateBinaryTree_1();
CreateBinaryTree_2(&bt);
PreviousOrderTraverse(bt);
return 0;
}
运行结果: