数据结构8:树和二叉树

一、树的逻辑结构

1.数的定义

:n(n>0)个结点的有限集合。当n=0时,称作空树;任意一棵非空树满足一下条件:

  • 有且仅有一个特定的成为根的结点。
  • 当n>1时,除跟接地单之外的其余结点被分成m(m>0)个互不相交的有限集合T1,T2,…,Tm。其中每个集合优势一棵树,并称为这个根节点的子树。(树的定义采用递归方法)

一棵树的结构

A
B
C
D
E
F
G
H
I

不是一棵树的结构1

A
B
C
D
F
G

不是一棵树的结构2

A
B
C
G
E
D
F

2.树的基本术语

  • 结点的度:结点所拥有的子树的个数。
  • 树的度:树中各节点度的最大值。
  • 叶子结点:度为0的结点,也称为终端结点。
  • 分支结点:度不为0的接地单,也成为了非终端结点。
  • 孩子、双亲:树中某结点子树的根节点称为这个结点的孩子结点,这个结点称为它孩子结点的双亲结点
  • 兄弟:具有同一个双亲的孩子结点互称兄弟。
  • 路径:如果数的结点序列n1,n2,…nk有如下关系:结点ni是ni+1的双亲(1<=i<k),则把n1,n2,…,nk称为一条由n1至nk的路径。注:路径上经过的边的个数称为路径长度
  • 祖先、孙子:在树中,如果有一条路径从结点x到结点y,则x称为y的祖先,而y称为x的孙子。
  • 结点所在层数:根节点的层数为1,对其余任何结点,若某结点在第k层,则其孩子结点在k+1层 。
  • 树的深度:树中所有节点的最大层数,也称为高度
  • 层序编号:将树中结点按照上层到下层、同层从左到右一次给他们以从1开始的自然连续数
A
B
C
D
E
F
G
H
I

上面的图中,ACBGFEDIH依次为123456789

  • 有序树、无序树:如果一棵树中结点的各子树从左到右是有次序的,称这棵树为有序树,反之为无序树。注:数据结构中讨论的一般都是有序树。
  • 森林:m(m>0)棵树互不相交的树的集合
A
B
C
D
E
F
G
H
I

3.树结构和线性结构的比较

①线性结构

  • 第一个数据元素(无前驱)
  • 最后一个数据元素(无后继)
  • 其他元素(一个前驱,一个后继)
  • 一对一

②树结构

  • 根节点(只有一个,无双亲)
  • 叶子节点(可以多个,无孩子)
  • 其他节点(一个双亲,多个孩子)
  • 一对多

4.树的抽象数据类型定义

​ 树的应用很广泛,在不同的实例应用中,数的基本操作不尽相同。下面给出一个树的抽象数据类型定义的例子,简单起见,基本操作只包含树的遍历,针对具体应用,需要重新定义基本操作。

ADT Tree
Data
	树是由一个根节点和若干棵子树构成,
	树中结点具有相同数据类型及层次关系。
Operation
InitTree
	前置套件:数不存在
	输入:无
	功能:初始化一棵树
	输出:无
	后置条件:构造一个空树
DestoryTree
	前置套件:数已存在
	输入:无
	功能:销毁一棵树
	输出:无
	后置条件:释放该数占用的存储空间
PerOrder
	前置套件:数已存在
	输入:无
	功能:前序遍历树
	输出:树的前序遍历序列
	后置条件:树保持不变
PostOrder
	前置套件:数已存在
	输入:无
	功能:后序遍历树
	输出:树的后序遍历序列
	后置条件:树保持不变
endADT

5.树的遍历操作

树的遍历:从根节点除法,按照某种次序访问树中的所有结点,使得每个结点被访问一次且仅被访问一次。

访问:抽象操作,可以是对结点进行各种操作,这里简化为输出结点的数据。

遍历的实质:树结构(非线性结构)—>线性结构。

次序:树通常有前序遍历、后序遍历与层序遍历

①前序遍历

树的前序遍历操作定义为:若树为空,则空操作返回;否则(1)访问根节点;(2)按照从左到右的顺序前序遍历根节点的每一棵子树。

A
B
C
D
E
F
G
H
I

前序遍历:ACGBFEIHD

②后序遍历

树的后序遍历操作定义为:若树为空,则空操作返回;否则(1)按照从左到右的顺序后序遍历根节点的每一棵子树。(2)访问根节点。

A
B
C
D
E
F
G
H
I

后序遍历:GCFIHEDBA

③层序遍历

层序遍历的定义为:从树的第一层(即根节点)开始,自上而下逐层遍历,在同一层中,按从左到右的顺序对接地点逐个访问。

A
B
C
D
E
F
G
H
I

层序遍历:ACBGFEDIH

二、树的存储结构

实现树的存储结构,关键是如何表示树中结点之间的逻辑关系。

存储结构:数据元素以及数据元素之间的逻辑关系在存储器中表示

树中结点之间的逻辑关系分为以下几种方法

1.双亲表示法

基本思想:用一位数组来存储树的各个结点(一般按照层序存储),数组中的一个元素对应树中的一个结点,包括结点的数据信息以及该节点的双亲数组中的下标。

data parent

data:存储树中结点的数据信息

parent:存储该节点的双亲在数组中的下标

struct PNode
{
	int data;//数据域
	int parent;//指针域,即双亲在数据中的下标
};

数的双亲表示法实质上时一个静态链表

A
B
C
D
E
F
G
H
I
下标 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

2.孩子链表表示法

链表中每一个结点包括一个数据域和多个指针域,每个指针指向该节点的一个孩子结点。

方案一:指针域的个数等于数的个数

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;
};

3.孩子兄弟表示法

firstchild data rightsib

data:数据域,存储该结点的数据信息。

firstchild:指针域,指向该结点第一个孩子。

rightsib:指针域,指向该结点的右兄弟结点。

strcut TNode
{
	int data;
	TNode *firstchild,*rightsib;
};

三、二叉树的定义

​ 二叉树是n(n>0)个结点的有限集合,该结合或者为空集(称为空二叉树),或者由一个根节点和两颗互不相交、分别称为根节点的左子树和右子树的二叉树组成。

1.特点

  • 每一个结点最多有两棵子树。
  • 二叉树是有序的,其次序不能任意颠倒

2.二叉树的基本形态

  • 空二叉树。
  • 只有一个根节点。
  • 根节点只有右子树。
  • 根节点只有左子树。
  • 根节点同时有左子树和右子树。

3.特殊的二叉树

①斜树

  • 所有结点都只有左子树的二叉树称为左斜树。
  • 所有结点都只有右子树的二叉树成为右斜树。
  • 左斜树和右斜树统称为斜树

②满二叉树

在一棵二叉树中,如果所有分支接地点都存在左子树和右子树,并且所有叶子都在同一层上。

特点

  • 叶子只能出现在最下层。
  • 只有度为0和度为2的结点。
  • 满二叉树在同样深度的二叉树中结点个数最多。
  • 满二叉树在同样深度的二叉树中叶子结点个数最多。

③完全二叉树

​ 对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同,则称为完全二叉树。满二叉树肯定是完全二叉树

​ 在满二叉树中,从最后一个节点开始,连续去掉任意个结点,即使一颗完全二叉树。

特点

  • 叶子结点只能出现在最下两层且最下层的结点都集中在二叉树的左面。
  • 完全二叉树中如果有度为1的结点,只能有一个,且该节点只有左孩子。
  • 深度为k的完全二叉树在k-1层上一定是满二叉树。
  • 在同样结点个数的二叉树中,完全二叉树的深度最小

4.二叉树的基本性质

①二叉树的第i层上最多有2i-1个结点(i>=1)。

②一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。

注:深度为k且具有2k-1个结点的二叉树一定是满二叉树。深度为k且具有k个结点的二叉树一定不是斜树。

③在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有:n0=n2+1.

④具有n个结点的完全二叉树的深度为log2n+1

⑤对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意序号为i(1<=i<=n)的结点(简称结点i),有:

  1. 如果i>1,则结点i的双亲结点的序号为i/2;如果i=1,则结点i是根节点,无双亲结点。
  2. 如果2i<n,则结点i的左孩子的序号为2i;如果2i>n,则结点无左孩子。
  3. 如果2i+1<=n,则结点i的右孩子的序号为2i+1;如果2i+1>n,则结点i无右孩子。

对一棵具有n个结点的完全二叉树中,从1开始按层序编号,则:

  • 结点i的双亲接地点为i/2;
  • 结点i的左孩子为2i;
  • 结点i的右孩子为2i+1;

性质五表名:在完全二叉树中,结点的层序编号反映了结点之间的逻辑关系。

5.二叉树的抽象数据类型定义

​ 同树类似,在不同的应用中,二叉树的基本操作不尽相同。下面是数据类型的例子。

ADT BiTree
Data
	由一个根节点和两颗互不相交的左右子树构成,
	结点具有相同数据类型及层次关系
Operation
InitBiTree
	前置条件:无
	输入:无
	功能:初始化一棵二叉树
	输出:无
	后置条件:构造一个空的二叉树
DestoryBiTree
	前置条件:二叉树已存在
	输入:无
	功能:销毁一棵二叉树
	输出:无
	后置条件:释放二叉树占用的存储空间
PreOrder
	前置条件:二叉树已存在
	输入:无
	功能:前序遍历二叉树
	输出:二叉树中结点的一个线性排列
	后置条件:二叉树不变
InOrder
	前置条件:二叉树已存在
	输入:无
	功能:中序遍历二叉树
	输出:二叉树中结点的一个线性排列
	后置条件:二叉树不变
PostOrder
	前置条件:二叉树已存在
	输入:无
	功能:后序遍历二叉树
	输出:二叉树中结点的一个线性排列
	后置条件:二叉树不变
LeverOrder
	前置条件:二叉树已存在
	输入:无
	功能:层序遍历二叉树
	输出:二叉树中结点的一个线性排列
	后置条件:二叉树不变

6.二叉树的遍历操作

​ 二叉树的遍历是指从跟姐地点出发,按照某种次序访问二叉树中所有结点,使得每个节点被访问一次且仅被访问一次。

方法:前序遍历、后续遍历、中序遍历与层序遍历。

①前序遍历

若二叉树为空,则空操作返回;否则:(1)访问根节点;(2)前序遍历根节点的左子树;(3)前序遍历根节点的右子树。

②中序遍历

若二叉树为空,则空操作返回;否则:(1)前序遍历根节点的左子树;(2)访问根节点;(3)前序遍历根节点的右子树。

③后序遍历

若二叉树为空,则空操作返回;否则:(1)前序遍历根节点的左子树;(2)前序遍历根节点的右子树;(3)访问根节点。

④层序遍历

二叉树层序遍历是指从二叉树的第一层(即根结点)

开始,从上至下逐层,在同一层,则按从左到右的顺序对结点逐个访问

7.拓展

​ 在已知一棵二叉树的前序序列和中序序列,构造该二叉树的过程如下:

  1. 根据前序序列的第一个元素建立根节点;
  2. 在中序序列中找到该元素,确定根节点的左右子树的中序序列;
  3. 在前序序列中确定左右子树的前序序列;
  4. 由左子树的前序序列和中序序列建立左子树;
  5. 由右子树的前序序列和中序序列建立右子树。

猜你喜欢

转载自blog.csdn.net/jiangSummer/article/details/106267391
今日推荐