数据结构学习笔记——树(一)


ps:此文章只是为了总结学习数据结构笔记,便于以后忘记查阅,因此部分图片会借用书上的图片,望理解。

(一)树的定义

树( Tree )是 n (n>=O) 个结点的有限集。n=0 时称为空树。 在任意一棵非空树中:

  • 有且仅有一个特定的称为根 ( Root )的结点:
  • 当 n>1 时,其余结点可分为 m (m>0) 个互不相变的有限集T1、T2、···、Tm,其中每个集合本身又是一棵树,并且称为根的子树(SubTree),如下图所示
       在这里插入图片描述

1、结点的分类

  树的结点包含一个数据元素及若干指向其他子树的分支。结点拥有的子树称为结点的度(Degree)。度为0 的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支节点。除根节点外,分支节点也称为内部结点。树的度是树内各结点的最大值。如下图所示,这棵树结点的度的最大值是D的度为3,因此树的度也为3.
   在这里插入图片描述

2、节点间关系

结点的子树的根称为该结点的孩子(Child),相应的该结点称为孩子的双亲(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根结点到该结点所经分支上的所有结点。如下图所示对于 H 来说, D、B、A都是他的祖先。反之,以某节点为根节点的子树中的任一结点都称为该结点的子孙。B 的子孙有D、G、H、I。

   在这里插入图片描述

3、树的其他相关概念

结点的层次 (LeveI) 从根开始定义起,根为第一层,根的孩子为第二层,双亲在同一层的结点直为堂兄弟。如下图中D、E、F是堂兄弟。**树中结点的最大层次称为树的深度(Depth)或高度。**当前树的深度为4。
   在这里插入图片描述
如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树,否则为无序树。
森林(Forest)是m(m>=0)棵互不相交的树的集合。

4、树与线性表的不同

树结构

  • 根节点:无双亲,唯一
  • 叶结点:无孩子,可以多个
  • 中间结点:一个双亲或多个孩子

线性结构

  • 第一个数据元素:无前驱
  • 最后一个数据元素:无后继
  • 中间元素:一个前驱一个后继

(二)树的基本操作

ADT 树 (tree)
Data
	树是由一个根结点和若干棵子树构成。 树中给点具有相同数据类型及层次关系。
Operation
	lnitTree ( *T ) 			:构造空树T
	DestroyTree ( *T ) 			:销毁树T.
	CreateTree (*T,definition) :按 definition 中给出树的定义来构造树。 
	ClearTree (*T) 				:若树 T 存在,则将树T清为空树。 
	TreeEmpty (T) 				:若 T 为空树,返回 true.否则返回主fa1se。
	TreeDepth (T) 				:返回 T 的深度。 
	Root (T) 					:返回 T 的根结点。
	Value ( T, cur_e) 			: cur_e 是树 T 中一个结点,返回此结点的值。 
	Assign (T, cur_e, value) 	:给树 T 的结点 cur_e 赋他为value。
	Parent (T, cur_e) 			:若 cur_e 是树 T 的非根结点. 别返回它的双亲,否则返回空。 
	LeftChild (T,cur_e)		:若 cur e是树T 的非叶结点,则返回它的最左孩子,否则返回空。 
	RightSibling (T, cur_e) :若 cur_e 有右兄弟.则返回它的右兄弟,否则返回空。 
	InsertChild (*T,*p,i, c)	:其中 p 指向树 T 的某个结点, i 为所指结点的度加上 1,非空树 c 与 T 不相交,操作结果为插入 c 为树 T 中 p 指结点的第 i 棵子树。
	DeleteChild (*T,*p, i) 	:其中 p 指向树 T 的某个结点,i-为所指给点 p 的度, 操作结果为删除 T 中 p 所指结点的第 i 裸子树。

endADT

(三)树的存储结构

  说到存储结构可能会想到前面所讲到的顺序存储和链式存储。充分利用顺序存储和链式存储的结构特点完全可实现对树的存储结构表示:双亲表示法、孩子表示法、孩子兄弟表示法。

1、双亲表示法

  每个结点不一定有孩子,但一定有双亲。我们假设以一组连续的空间存储树的结点,同时在每个节点中,附设一个指示器指示其双亲结点到链表中的位置。也就是说每个结点除了知道自己是谁以外还知道他的双亲在哪里。
               在这里插入图片描述
  其中data为数据域,存储结点的数据信息,parent为指针域,存储结点的双亲在数组中的下标。
双亲表示法的节点结构:

#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */
typedef int TElemType;  /* 树结点的数据类型,目前暂定为整型 */
typedef struct PTNode /*结点结构*/
{
	TElmType data;   //结点结构
	ìnt parent;		 //双亲位置
}PTNode;
typedef struct       //树结构
{
	PTNode nodes [MAX_TREE_SIZE) ;   //结点数组
	int r,n;						 //根的位置和节点数
}PTree;

  树结构的双亲表示法如下图所示
   在这里插入图片描述
  这样的存储结构可根据结点的Parent指针找到双亲结点,其时间复杂度为O(1),但如果要找结点的孩子则需要遍历整个数组。为了解决这个问题我们可给其节点结构再加一个孩子域(可为左孩子域也可为右孩子域):
   在这里插入图片描述

2、孩子表示法

  换个思路,由于树中每个结点可能有多棵子树,可以考虑多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根节点,我们能把这种方法叫做多重链表表示法。树的每个结点的度也就是他的孩子个数不同分两种情况解决:

  • 方案一
      一种是指针域的个树就等于树的度(树的度就是树各个结点度的最大值:下图中data为数据域,child1到childd为指针域指向该结点的孩子节点:
    在这里插入图片描述
      因为该种方案很多指针域都为空我们可以考虑下按需分配,下面看第二种方案:
  • 方案二
      第二种方案每个结点的指针域的个数等于该结点的度,我们专门取一个位置来存储结点指针域的个数:data为数据域,degree为度域即存放孩子节点的个数,child1到childd为指针域:
    在这里插入图片描述
      这个方法虽然解决了浪费空间的缺点但由于各个节点链表不同结构,会带来时间上的损耗。我们为了要遍历整棵树,把每个结点放到一个顺序存储结构的数组中是合理的,但每个结点的孩子有多少是不确定的,所以我们再对每个结点的孩子建立 一个单链表体现它们的关系。即:把每个结点的孩子结点排列起来, 以单链表作存储结构,则 n 个结点有 n 个孩子链表,如果是叶子结点则此单链表为空。然后 n 个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。如下图所示:
       在这里插入图片描述
     为此,设计两种结点结构, 一个是孩子链表的孩子结点:其中 child是数据域,用来存储某个结点在表头数组中的下标。 next 是指针域,用 来存储指向某结点的下一个孩子结点的指针。另一个是表头数组的表头结点,其中 data 是数据域,存储某结点的数据信息。 firstchild 是头指针域, 存储该结点的孩子链表的头指针。
          在这里插入图片描述
      在上面的结构我们要想知道某个结点的孩子只需查找该结点的单链表如果我们想知道该节点的双亲只需稍微优化下将刚才的结构即可得到双亲孩子表示法
        在这里插入图片描述
    双亲孩子表示法的结构定义:
//数的双亲孩子表示法结点结构定义
#define MAX_TREE_SIZE 100
 
type char ElemType;  //结点的数据类型为  char 类型 
 
//定义一个孩子结点 
typedef struct CTNode
{
	int child;             //孩子结点的下标
	struct CTNode *next;   //指向下一个孩子结点的指针 
} *ChildPtr;
 
//表头结构 
typedef struct
{
	ElemType data;        //存放在树中的结点的数据A,B,C。。。。 
	int parent;           //存放双亲的下标 
	ChildPtr firstchild;  //指向第一个孩子结点的指针 
} CTBox;
 
//树结构
typedof struct
{
	CTBox nodes[MAX_TREE_SIZE];   //结点数组 
	int r,n; //分别是根的位置和结点的数 
} 

2、孩子兄弟表示法

  通过研究树结构我们可发现:任意一棵树, 它的结点的第一个孩子如果存在就是唯一 的,它的右兄弟如果存在也是唯一的。 因此,我们设置两个指针, 分别指向该结点的 第一个孩子和此结点的右兄弟。
            在这里插入图片描述
  其中data为数据域,firstchild为指针域,存储该节点的第一个孩子结点的存储地址,rightsib为指针域,存储该结点的右兄弟的存储地址。

/*树的孩子兄弟表示法结构定义 */
typedef struct CSNode 
{
	TElemType data; 
	struct CSNode *firstchild, *rightsib;
}CSNode, *CSTree;

  孩子兄弟表示法通过两个指针域可以知道该结点的长子和二弟其结构示意图如下:
   在这里插入图片描述
  这个表示法的最大好处就是把一颗复杂的树标称了一颗二叉树,可通过二叉树的特性和算法来处理了。

参考书籍:《大话数据结构》

猜你喜欢

转载自blog.csdn.net/weixin_42647166/article/details/104706746
今日推荐