树:n(n>0)个结点的有限集合。当n=0时,称作空树;任意一棵非空树满足一下条件:
一棵树的结构
不是一棵树的结构1
不是一棵树的结构2
上面的图中,ACBGFEDIH依次为123456789
树的应用很广泛,在不同的实例应用中,数的基本操作不尽相同。下面给出一个树的抽象数据类型定义的例子,简单起见,基本操作只包含树的遍历,针对具体应用,需要重新定义基本操作。
ADT Tree
Data
树是由一个根节点和若干棵子树构成,
树中结点具有相同数据类型及层次关系。
Operation
InitTree
前置套件:数不存在
输入:无
功能:初始化一棵树
输出:无
后置条件:构造一个空树
DestoryTree
前置套件:数已存在
输入:无
功能:销毁一棵树
输出:无
后置条件:释放该数占用的存储空间
PerOrder
前置套件:数已存在
输入:无
功能:前序遍历树
输出:树的前序遍历序列
后置条件:树保持不变
PostOrder
前置套件:数已存在
输入:无
功能:后序遍历树
输出:树的后序遍历序列
后置条件:树保持不变
endADT
树的遍历:从根节点除法,按照某种次序访问树中的所有结点,使得每个结点被访问一次且仅被访问一次。
访问:抽象操作,可以是对结点进行各种操作,这里简化为输出结点的数据。
遍历的实质:树结构(非线性结构)—>线性结构。
次序:树通常有前序遍历、后序遍历与层序遍历
树的前序遍历操作定义为:若树为空,则空操作返回;否则(1)访问根节点;(2)按照从左到右的顺序前序遍历根节点的每一棵子树。
前序遍历:ACGBFEIHD
树的后序遍历操作定义为:若树为空,则空操作返回;否则(1)按照从左到右的顺序后序遍历根节点的每一棵子树。(2)访问根节点。
后序遍历:GCFIHEDBA
层序遍历的定义为:从树的第一层(即根节点)开始,自上而下逐层遍历,在同一层中,按从左到右的顺序对接地点逐个访问。
层序遍历:ACBGFEDIH
实现树的存储结构,关键是如何表示树中结点之间的逻辑关系。
存储结构:数据元素以及数据元素之间的逻辑关系在存储器中表示
树中结点之间的逻辑关系分为以下几种方法
基本思想:用一位数组来存储树的各个结点(一般按照层序存储),数组中的一个元素对应树中的一个结点,包括结点的数据信息以及该节点的双亲数组中的下标。
data parent
data:存储树中结点的数据信息
parent:存储该节点的双亲在数组中的下标
struct PNode
{
int data;//数据域
int parent;//指针域,即双亲在数据中的下标
};
数的双亲表示法实质上时一个静态链表
下标 | data | parent |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 1 |
4 | E | 1 |
5 | F | 1 |
6 | G | 2 |
7 | H | 4 |
8 | I | 4 |
为了方便查找,我们也可以使用第一个孩子的结点进行表示
下标 | data | parent | firstchild |
---|---|---|---|
0 | A | -1 | 1 |
1 | B | 0 | 5 |
2 | C | 0 | 6 |
3 | D | 1 | -1 |
4 | E | 1 | 8 |
5 | F | 1 | -1 |
6 | G | 2 | - |
7 | H | 4 | - |
8 | I | 4 | - |
如何查找兄弟结点?
下标 | data | parent | rightsib |
---|---|---|---|
0 | A | -1 | -1 |
1 | B | 0 | -1 |
2 | C | 0 | 1 |
3 | D | 1 | -1 |
4 | E | 1 | 3 |
5 | F | 1 | 4 |
6 | G | 2 | -1 |
7 | H | 4 | -1 |
8 | I | 4 | 7 |
链表中每一个结点包括一个数据域和多个指针域,每个指针指向该节点的一个孩子结点。
data child1 child2 child3 ... childn
data:数据域,存储该结点的数据信息。
child1~childn:指针域,指向该结点的孩子。
缺点:浪费空间
data degree child1 child2 child3 ... childn
data:数据域,存储该结点的数据信息。
degree:度域,存放该结点的度。
child1~childn:指针域,指向该结点的孩子。
缺点:结点结构不一致
将结点的所有孩子放在一起,构成线性表。
孩子链表的基本思想:把每个节点的孩子排列起来,看成是一个线性表,且以单链表存储,则n个结点共有n个孩子链表。这n个单链表共有n个头指针,这n个头指针有组成了一个线性表,为了便于进行查找采用顺序存储。最后,将存放n个头指针的数据和存放n个结点的数组结合起来,构成孩子链表的表头。
//孩子结点
struct CTNode
{
int child;
CTNode *next;
};
//表头结点
struct CBNode
{
int data;
CTNode *firstchild;
};
firstchild data rightsib
data:数据域,存储该结点的数据信息。
firstchild:指针域,指向该结点第一个孩子。
rightsib:指针域,指向该结点的右兄弟结点。
strcut TNode
{
int data;
TNode *firstchild,*rightsib;
};
二叉树是n(n>0)个结点的有限集合,该结合或者为空集(称为空二叉树),或者由一个根节点和两颗互不相交、分别称为根节点的左子树和右子树的二叉树组成。
在一棵二叉树中,如果所有分支接地点都存在左子树和右子树,并且所有叶子都在同一层上。
特点:
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同,则称为完全二叉树。满二叉树肯定是完全二叉树。
在满二叉树中,从最后一个节点开始,连续去掉任意个结点,即使一颗完全二叉树。
特点:
注:深度为k且具有2k-1个结点的二叉树一定是满二叉树。深度为k且具有k个结点的二叉树一定不是斜树。
对一棵具有n个结点的完全二叉树中,从1开始按层序编号,则:
性质五表名:在完全二叉树中,结点的层序编号反映了结点之间的逻辑关系。
同树类似,在不同的应用中,二叉树的基本操作不尽相同。下面是数据类型的例子。
ADT BiTree
Data
由一个根节点和两颗互不相交的左右子树构成,
结点具有相同数据类型及层次关系
Operation
InitBiTree
前置条件:无
输入:无
功能:初始化一棵二叉树
输出:无
后置条件:构造一个空的二叉树
DestoryBiTree
前置条件:二叉树已存在
输入:无
功能:销毁一棵二叉树
输出:无
后置条件:释放二叉树占用的存储空间
PreOrder
前置条件:二叉树已存在
输入:无
功能:前序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
InOrder
前置条件:二叉树已存在
输入:无
功能:中序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
PostOrder
前置条件:二叉树已存在
输入:无
功能:后序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
LeverOrder
前置条件:二叉树已存在
输入:无
功能:层序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
二叉树的遍历是指从跟姐地点出发,按照某种次序访问二叉树中所有结点,使得每个节点被访问一次且仅被访问一次。
方法:前序遍历、后续遍历、中序遍历与层序遍历。
若二叉树为空,则空操作返回;否则:(1)访问根节点;(2)前序遍历根节点的左子树;(3)前序遍历根节点的右子树。
若二叉树为空,则空操作返回;否则:(1)前序遍历根节点的左子树;(2)访问根节点;(3)前序遍历根节点的右子树。
若二叉树为空,则空操作返回;否则:(1)前序遍历根节点的左子树;(2)前序遍历根节点的右子树;(3)访问根节点。
④层序遍历
二叉树层序遍历是指从二叉树的第一层(即根结点)
开始,从上至下逐层,在同一层,则按从左到右的顺序对结点逐个访问
在已知一棵二叉树的前序序列和中序序列,构造该二叉树的过程如下: