目录
1. 树和二叉树的定义
1.1 树的定义
- 树(Tree): n ( n ≥ 0 )个结点的有限集
- 空树:n = 0
- 非空树 T :
- 有且只有一个称之为根的结点
- 除根结点外的其余结点可分为 m ( m > 0)个互不相交的有限集 T_1 ,T_2 ,··· ,T_m
- 其中每个集合本身又是一颗树,称为根的子树(SubTree)
1.2 树的基本术语
- 结点:树种的一个独立单元,包含一个数据元素及若干指向其子树的分支
- 结点的度:结点拥有的子树数量
- 树的度:树内各结点度的最大值
- 叶子(终端结点):度为 0 的结点
- 非终端结点(分支结点):度不为 0 的结点;除根节点外,非终端结点也称为内部结点
- 双亲和孩子:
- 结点的子树的根称为该结点的孩子
- 该结点称为孩子的双亲
- 兄弟:同一个双亲的孩子之间互称兄弟
- 祖先:从根到该结点所经分支上的所有结点
- 子孙:以某结点为根的子树种的任一结点
- 层次:从根开始定义起,根为第一层,根的孩子为第二层,依次类推
- 堂兄弟:双亲在同一层的结点互为堂兄弟
- 树的深度(高度):树中结点的最大层次
- 有序树和无序树:
- 有序树:树中结点的各子树是从左至右有次序的(不能互换)
- 第一个孩子:最左边的子树的根
- 最后一个孩子:最右边的子树的根
- 无序树:与有序树相反
- 有序树:树中结点的各子树是从左至右有次序的(不能互换)
- 森林:m ( m ≥ 0 )棵互不相交的树的集合
1.3 二叉树的定义
-
二叉树(Binary Tree):n ( n ≥ 0 )个结点所构成的集合
- 空树:n = 0
- 非空树 T :
- 有且只有一个称之为根的结点
- 除根结点外的其余结点分为两个互不相交的子集 T_1 和 T_2 ,分别称为 T 的左子树和右子树
- T_1 和 T_2 本身又都是二叉树
-
二叉树和树的区别
- 二叉树每个结点至多只有两棵子树
- 二叉树中不存在度大于 2 的结点
- 二叉树的子树有左右之分,其次序不能任意颠倒
- 二叉树每个结点至多只有两棵子树
-
二叉树的 5 种基本形态
- 空二叉树
- 仅有根结点的二叉树
- 右子树为空的二叉树
- 左、右子树均非空的二叉树
- 左子树为空的二叉树
2. 二叉树的性质和存储过程
2.1 二叉树的性质
-
满二叉树:深度为 k 且含有 2^k - 1 个结点
- 每一层上的结点数都是最大的结点数
- 每一层 i 的结点数都具有最大值 2^(i-1)
- 每一层上的结点数都是最大的结点数
-
完全二叉树:深度为 k 的,有 n 个结点的二叉树,每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应
- 叶子结点只可能是在层次最大的两层上出现
- 对任一结点,若其右分支下的子孙的最大层次为 l ,则其左分支下的子孙的最大层次必为 l 或 l+1
-
重要性质
-
性质 1 :在二叉树的第 i 层上至多有 2^(i-1) 个结点(i ≥ 1)
-
性质 2 :深度为 k 的二叉树至多有 2^k - 1 个结点(k ≥ 1)
-
性质 3 :对任何一颗二叉树 T ,如果其终端结点数为 n_0 ,度为 2 的结点数为 n_2 ,则 n_0 = n_2 + 1
n = n 0 + n 1 + n 2 n = n_0 + n_1 + n_2 n=n0+n1+n2n = ( 1 n 1 + 2 n 2 ) + 1 n = (1n_1 + 2n_2) + 1 n=(1n1+2n2)+1
n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
- 性质 4 :具有 n 个结点的完全二叉树的深度为 ⌊ log_2 (n) ⌋ + 1
- 性质 5 :如果对一棵有 n 个结点的完全二叉树(其深度为 ⌊ log_2 (n) ⌋ + 1 )的结点按层序编号(从第 1 层到第 ⌊ log_2 (n) ⌋ + 1 层,每层从左到右 ),则对任一结点 i ( 1 ≤ i ≤ n )
- i = 1 :结点 i 是二叉树的根,无双亲
- i > 1 :其双亲是结点 ⌊ i/2 ⌋
- 2i > n :结点 i 无左孩子(结点 i 为叶子结点)或左孩子为结点 2i
- 2i + 1 > n :结点 i 无右孩子或右孩子为结点 2i+1
-
2.2 二叉树的存储结构
2.2.1 顺序存储结构
#define MAXSIZE 10 // 二叉树的最大结点数
typedef TElemType SqBiTree[MAXSIZE]; // 0 号单元存储根结点
SqBiTree bt;
- 对于完全二叉树:从根起按层序存储,依次自上而下、从左至右存储结点元素
- 将完全二叉树上编号为 i 的结点元素存储在如上定义的一维数组中下标为 i-1 的分量中
- 对于一般二叉树:将其每个结点与完全二叉树上的结点相对照,存储在一维数组的相应分量中
2.2.2 链式存储结构
- 构成
- 数据域
- 左指针域
- 右指针域
- 指向双亲结点的指针域
- 在含有 n 个结点的二叉链表中有 n+1 个空链域
typedef struct BiTNode {
ElemType elem; // 结点数据域
struct BiTNode *lchild; // 左孩子指针
struct BiTNode *rchild; // 右孩子指针
} BiTNode, *BiTree;
3. 遍历二叉树和线索二叉树
3.1 遍历二叉树
-
遍历二叉树(traversing ):按某条搜索路径巡防树种每个结点,使得每个结点均被访问一次,且仅被访问一次
-
先序遍历二叉树
- 访问根节点
- 先序遍历左子树
- 先序遍历右子树
-
中序遍历二叉树
- 中序遍历左子树
- 访问根节点
- 中序遍历右子树
-
后序遍历二叉树
- 后序遍历左子树
- 后序遍历右子树
- 访问根节点
-
先序遍历:从上至下,从左至右
-
中序遍历:从左至右,从上至下
-
后序遍历:从下至上,从左至右
3.1.1 中序遍历的递归算法
void InOrderTraverse(BiTree T) {
if (T) {
InOrderTraverse(T->lchild); // 中序遍历左子树
printf("value = %d\n", T->value); // 访问根节点
InOrderTraverse(T->rchild); // 中序遍历右子树
}
}
3.1.2 中序遍历的非递归算法
- 步骤
- 初始化一个空栈 S ,指针 p 指向根结点
- 申请一个结点空间 q ,用来存放栈顶弹出的元素
- 当 p 非空或栈 S 非空时,循环执行以下操作
- p 非空,p 进栈,p 指向该结点的左孩子
- p 为空,弹出栈顶元素并访问,将 p 指向该结点的右孩子
void InOrderTraverse(BiTree T) {
BiTree* p = T;
BiNode q = (BiNode*)malloc(sizeof(BiNode));
InitStack(S);
while (p || StackEmpty(S)) {
if (p) { // p 非空
Push(S, p); // 根指针进栈
p = p->lchild; // 遍历左子树
} else {
Pop(S, q); // 退栈
printf("value = %d\n", T->value); // 访问根节点
p = q->rchild; // 遍历右子树
}
}
}
3.1.3 先序遍历的顺序建立二叉链表
void CreateBiTree(BiTree* T) {
int value;
printf("Enter : ");
scanf("%d", &value);
if (value == 0) {
(*T) = NULL; // 递归结束,创建空树
}
else {
(*T) = (BiTNode*)malloc(sizeof(BiTNode)); // 生成根结点
(*T)->value = value; // 根结点数据域置为 value
CreateBiTree(&((*T)->lchild)); // 递归创建左子树
CreateBiTree(&((*T)->rchild)); // 递归创建右子树
}
}
3.1.4 复制二叉树
void Copy(BiTree T, BiTree* NewT) {
if (T == NULL)
{
*NewT = NULL; // 递归结束
}
else {
*NewT = (BiTNode*)malloc(sizeof(BiTNode));
(*NewT)->value = T->value; // 复制根结点
printf("copy value = %d\n", T->value);
Copy(T->lchild, &(*NewT)->lchild); // 递归复制左子树
Copy(T->rchild, &(*NewT)->rchild); // 递归复制右子树
}
}
3.1.5 计算二叉树的深度
int Depth(BiTree T) {
if (T)
{
int i = Depth(T->lchild); // 递归计算左子树的深度记为 i
int j = Depth(T->rchild); // 递归计算右子树的深度记为 j
return (i > j) ? i + 1 : j + 1;
}
else
{
return 0; // 递归结束,深度为 0
}
}
3.1.6 统计二叉树中结点的个数
int NodeCount(BiTree T) {
return T ? NodeCount(T->lchild) + NodeCount(T->rchild) + 1 : 0;
// T 不为空 : 结点个数 = 左子树的结点个数 + 右子树结点个数 + 1
// T 为空 : 结点个数为 0 ,递归结束
}
测试代码
#include <stdio.h>
#include <stdlib.h>
void InOrderTraverse(BiTree);
void CreateBiTree(BiTree);
void Copy(BiTree, BiTree);
int Depth(BiTree);
int NodeCount(BiTree);
typedef struct BiTNode {
int value;
struct BiTNode* lchild;
struct BiTNode* rchild;
} BiTNode, *BiTree;
int main() {
BiTree tree1;
BiTree tree2;
CreateBiTree(&tree1);
printf("Create Success\n");
printf("****************\n");
InOrderTraverse(tree1);
printf("Traverse Success\n");
printf("****************\n");
Copy(tree1, &tree2);
printf("Copy Success\n");
printf("****************\n");
InOrderTraverse(tree2);
printf("Traverse Success\n");
printf("****************\n");
int depth = Depth(tree1);
printf("depth = %d\n", depth);
printf("****************\n");
int count = NodeCount(tree1);
printf("count = %d\n", count);
printf("****************\n");
}
void InOrderTraverse(BiTree T) {
if (T) {
InOrderTraverse(T->lchild);
printf("value = %d\n", T->value);
InOrderTraverse(T->rchild);
}
}
void CreateBiTree(BiTree* T) {
int value;
printf("Enter : ");
scanf("%d", &value);
if (value == 0) {
*T = NULL;
}
else {
*T = (BiTNode*)malloc(sizeof(BiTNode));
(*T)->value = value;
CreateBiTree(&((*T)->lchild));
CreateBiTree(&((*T)->rchild));
}
}
void Copy(BiTree T, BiTree* NewT) {
if (T == NULL)
{
*NewT = NULL;
}
else {
*NewT = (BiTNode*)malloc(sizeof(BiTNode));
(*NewT)->value = T->value;
printf("copy value = %d\n", T->value);
Copy(T->lchild, &(*NewT)->lchild);
Copy(T->rchild, &(*NewT)->rchild);
}
}
int Depth(BiTree T) {
if (T)
{
int i = Depth(T->lchild);
int j = Depth(T->rchild);
return (i > j) ? i + 1 : j + 1;
}
else
{
return 0;
}
}
int NodeCount(BiTree T) {
return T ? NodeCount(T->lchild) + NodeCount(T->rchild) + 1 : 0;
}