3.1 树型结构
(1)树
1、树的定义
- 树是n(n≥0)的有限集(树的定义是一个递归的定义(循环地调用自己))
- 若n=0,则称为空树
- 若n>0,则它满足如下两个条件
- 有且仅有一个特定的称为根(root)的结点
- 其余结点可分为m(m≥0)个互不相交的有限集,,…,,其中每一个集合本身又是一棵树,被称为根的子树(SubTree)
2、相关术语
- 根结点:非空树中无前驱结点的的结点
- 结点的度:结点拥有的子树数
- 树的度:树内各结点的度的最大值
- 树的深度:树中结点的最大层次(树的高度)
- 叶子:终端结点,度=0
- 分支结点:非终端结点,度≠0
- 内部结点:根结点以外的分支结点
- 结点的孩子:结点的子树的根,该结点为孩子的双亲
- 堂兄弟:双亲在同一层的结点
- 结点的祖先:从根到该结点所经分支上的所有结点
- 结点的子孙:以某结点为根的子树中的任一结点
- 有序树:树中结点的各子树从左到右有次序
- 无序树:树中结点的各子树无次序
- 森林:是m(m≥0)棵互不相交的树的集合
- 树是森林的充分不必要条件(树一定是森林,森林不一定是树)
- 一棵树可以看成是一个特殊的森林
- 把根结点删除,树就变成了森林
- 给森林中的各子树加上一个双亲结点,森林就变成了树
- 树是森林的充分不必要条件(树一定是森林,森林不一定是树)
3、树的存储结构
① 双亲表示法
- 实现:定义结构数组存放树的结点,每个结点含两个域
- 数据域:存放结点本身信息
- 双亲域:指示本结点的双亲结点在数组中的位置
- 存储结构定义
结点结构:data | parent
typedef struct PTNode{
TElemType data;
int parent; //双亲位置域
}PTNode;
树结构:
#define MAX_TREE_SIZE 100
typedef struct{
PTNode nodes[MAX_TREE_SIZE];
int r,n; //根结点的位置和结点的个数
}PTree;
- 缺点:找双亲容易,找孩子难
② 孩子链表
- 实现:把每个结点的孩子结点排列起来,看成是一个线性表,用单链表存储
- 则n个结点有n个孩子链表(叶子的孩子链表为空表)
- 而n个头指针又组成一个线性表,用顺序表存储(含n个元素的结构数组)
- 存储结构定义
孩子结点结构:child | next
typedef struct CTNode{
int child;
struct CTNode *next;
}*ChildPtr;
双亲结点结构:data | firstchild
typedef struct{
TElemType data;
ChildPtr firstchild; //孩子链表头指针
}CTBox;
树结构:
#define MAX_TREE_SIZE 100
typedef struct{
CTBox node[MAX_TREE_SIZE];
int n,r; //结点数和根结点的位置
}CTree;
- 缺点:找孩子容易,找双亲难
- 解决方法:带双亲的孩子链表(在双亲结点结构加一个双亲域,指向双亲的结点位置 )
③ 孩子兄弟表示法(二叉树表示法,二叉链表表示法)
- 实现:用二叉链表作树的存储结构,链表中每个结点的两个指针域,分别指向其第一个孩子结点和下一个兄弟结点
- 存储结构定义
结点结构:孩子域 | data | 兄弟域
typedef struct CSNode{
TElemType data;
struct CSNode *firstchild,*nextsibling;
}CSNode;*CSTree;
(2)二叉树
1、二叉树的定义
- 二叉树是n(n>=0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的分别称为这个根的左子树和右子树的二叉树组成
2、二叉树的特点
- 每个结点最多有两个孩子(二叉树中不存在度大于2的结点)
- 子树有左右之分,次序不能颠倒
- 二叉树可以是空集合,根可以有空的左子树或空的右子树
- 二叉树和树是两个概念
- 二叉树中每个结点位置或者说次序都是固定的,可以是空但不能说它没有位置
- 而树的结点位置是相对于别的结点来说,没有别的结点时,它就无所谓左右了
- 二叉树和树是两个概念
3、两种特殊形式的二叉树
- 满二叉树
- 定义:一棵深度为k且有个结点的二叉树
- 特点
- 每一层上的结点数都是最大结点数(即每层都满)
- 叶子结点全部在最底层
- 结点编号规则:自上而下,自左而右,每个结点对应一个编号
- 完全二叉树
- 定义:深度为k的具有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应
- 注:在满二叉树中,从最后一个结点开始,连续的去掉任意个结点,即是一棵完全二叉树
- 特点
- 叶子结点只可能分布在层次最大的两层上
- 对任意结点,如果右子树的最大层次为i,则其左子树的最大层次必为i或i+1
- 定义:深度为k的具有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应
4、二叉树的性质
- 在二叉树的第i层上至多有个结点(i>=1)
- 第i层上至少有一个结点
- 深度为k的二叉树至多有结点(k>=1)
- 深度为k的二叉树至少有k个结点
- 对任何一棵二叉树T,如果其叶子数为,度为2的结点数为,则=+1
- B(总边数)=n(总结点数)-1
- B(总边数)=(度为2的结点数)*2+(度为1的结点数)*1
- n=2*++1,又n=++,故=+1
- 具有n个结点的完全二叉树的深度为⌊ ⌋+1
- ⌊X⌋:称作X的底,表示不大于X的最大整数
- 性质4表明了完全二叉树结点数n与完全二叉树深度k之间的关系
- 如果对一棵又有n个结点的完全二叉树(⌊⌋+1)的结点按层序编号(从第1层到第⌊⌋+1层,每层从左到右),则对任一结点i(1<=i<=n),有:
- 如果i=1,则结点是二叉树的根,无双亲;如果i>1,则双亲是结点⌊i/2⌋
- 如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i
- 如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1
- 性质5表明了完全二叉树中双亲结点编号与孩子结点编号之间的关系
5、二叉树的存储结构
① 顺序存储结构
- 实现:按满二叉树的结点层次编号,依次存放二叉树中的数据元素
- 特点
- 缺点:浪费空间,适用于存满二叉树和完全二叉树(最坏情况下,深度为k的且只有k个结点的单支树需要长度为的一维数组,存储密度小)
- 优点:结点间关系蕴含在其存储位置中
#define MAXSIZE 100
typedef TElemType SqBiTree[MAXSIZE];
SqBiTree bt;
② 链式存储结构
- 二叉链表
- 思考:在n个结点的二叉链表中,有n+1个空指针域
- 存储结构定义
结点结构:lchild | data | rchild
typedef struct BiNode{
TElemType data;
struct BiNode *lchild,*rchild; //左右孩子指针
}BiNode,*BiTree;
- 三叉链表
- 存储结构定义
结点结构:lchild | data | parent | rchild
typedef struct TriNode{
TElemType data;
struct TriNode *lchild,*rchild,*parent;
}TriNode,*TriTree;
6、二叉树遍历算法