大话数据结构11之树

1、树的定义:是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:<1>有且仅有一个特定的称为根(Root)的结点;<2>当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、......、Tm,其中每一个集合本身又是一颗树,并且称为根的子树。注:n>0时根结点是唯一的,不可能存在多个根节点。

2、结点分类:根的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。

3、结点间的关系:结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根结点到该结点所经分支上的所有结点,以某结点为根的子树中的任一结点都称为该结点的子孙。

4、树的其他相关概念:结点的层次(Level)从根开始定义起,根为第一层,根的孩子结点为第二层。双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度。

    如果将树中结点的各个子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。

    森林是m(m>=0)课互不相交的树的集合。对于树中的每一个结点而言,其子树的集合即为森林。

5、对比线性表与树的结构:线性表:第一个数据元素无前驱;最后一个数据元素无后继;中间元素一个前驱一个后继;

树结构:根结点无双亲唯一;叶节点无孩子,可以多个;中间结点一个双亲多个孩子。

6、树的抽象数据类型

	ADT 树(tree)
	Data
		树是由一个根结点和若干课子树构成。树中结点具有相同数据类型及层次关系。
	Operation
		InitTree(*T):构造空树T。
		DestroyTree(*T):销毁树T。
		CreateTree(*T,definition):按defeinition中给出树的定义来构造树。
		ClearTree(*T):若树存在,则将树T清为空树。
		TreeEmpty(T):若T为空树,返回true,否则返回false。
		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为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p指结点的第i课子树。
		DeleteChild(*T,*p,i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i课子树。
	endADT

7、树的存储结构

<1>双亲表示法:假设以一组连续空间存储树的结点,同时在每一个结点中,附设一个指示器指示其双亲结点在数组中的位置。一个data域表示的是数据域,存储结点的数据信息;parent域是指针域,存储该结点的双亲在数组中的下标。

  双亲表示法的结点结构定义代码:

	    /*树的双亲表示法结点结构定义*/
	    #define MAX_TREE_SIZE 100
	    typedef int TElemType;      /*树结点的数据类型,目前暂定为整型*/
	    typedef struct PTNode      /*结点结构*/
	    {
		  TElemType data;     /*结点数据*/
		  int parent;       /*双亲位置*/
	    }PTNode;
	    typedef struct         /*树结构*/
	    {
		  PTNode nodes[MAX_TREE_SIZE];  /*结点数组*/
	       int r,n;               /*根的位置和结点数*/
	    }PTree;

注:根结点没有双亲,所以约定根结点是我位置域设置为-1,则所有的结点都存有它双亲的位置。因此我们可以根据指针的parent域找到它的双亲结点,所用的时间复杂度为O(1),但是要知道结点的孩子是什么则需要遍历整个结构。可以针对这一缺陷有所改进:增加一个结点最左边孩子的域,即长子域,如果没有孩子结点,长子域就设定为-1。

<2>孩子表示法:

    多重链表表示法:因为树中每一个结点可能有多棵子树,则我们考虑用多重链表来表示,即每一个结点有多个指针域,其中每个指针指向一颗子树的根结点。

1)指针域的个数就等于树的度

  缺点:如果树中的度相差很大时,很浪费空间,但是如果相差很小就可以充分利用空间。

2)每个结点指针域的个数等于该结点的度,专门取一个位置来存储结点指针域的个数。这是在上一中方法上的改进以实现按需分配空间。

  优点:提高空间利用率

  缺点:由于各个结点的链表是不同结构的,加上要维护结点的度的值,会给运算带来时间上的损耗。

  孩子表示法就可以减少空指针的浪费也可以使结点结构相同。把每个结点的孩子结点排列起来以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。

于是有两个结构:一个是孩子链表的孩子结点,其中child域是数据域,用来存储每个结点在表头数组中的下标。next是指针域,用来存储指向某结点的下一个孩子结点的指针。另一个是表头数组的表头结点,其中data是数据域,存储某结点的数据信息,firstchild是头指针域,存储该结点的孩子链表的头指针。

孩子表示法的结构定义代码:

		/*树的孩子表示法结构定义*/
		#define MAX_TREE_SIZE 100
		typedef struct CTNode  /*孩子结点*/
		{
			int child;
			struct CTNode *next;
		}*ChildPtr;
		typedef struct   /*表头结构*/
		{
			TElemType data;
			ChildPtr firstchild;
		}CTBox;
		typedef struct
		{
			CTBox nodes[MAX_TREE_SIZE];  /*结点数组*/
			int r,n;     /*根的位置和结点数*/
		}CTree;

因此,查找某个结点的某个孩子,或者找某个结点的兄弟,只需要查找这个结点的孩子单链表,对于遍历整棵树,只需要对头结点的数组循环即可。

缺点:查询某结点的双亲需要遍历整棵树。可以综合双亲表示法和孩子表示法,用双亲孩子表示法。

<3>孩子兄弟表示法:任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。

data是数据域,firstchild是指针域,存储该结点的第一个孩子的存储地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址。

结构定义代码:

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


优点:查找某结点的某孩子方便,只需要通过firstchild找到此结点的长子,然后再通过长子结点的rightsib找到它的二弟,接着一直下去,直到找到具体的孩子。

缺点:查询某结点的双亲时比较麻烦,改进增加一个parent指针。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值