05 树

05 树

05 树_第1张图片

定义

  1. 度:结点拥有的子树数称为结点的度(Degree)

  2. 孩子:结点的子树的根称为该结点的孩子(Child),相应的,该结点称为孩子的双亲(Parent)

  3. 兄弟:同一个双亲的孩子之间互称兄弟(Sibling)

  4. 结点的祖先:从根到该结点所经分支上的所有结点

  5. 子孙:以某结点为根的子树中的任一结点都称为该结点的子孙

  6. 结点的层次:从根开始定义,根为第一层,根的孩子为第二层

  7. 堂兄弟:双亲在同一层的结点互为堂兄弟

  8. 深度:树中结点的最大层次称为树的深度(Depth)或高度

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

  10. 森林:森林(Forest)是m(m>=0)棵互不相交的树的集合

    ADT 树(Tree)
    	Data
    		树是由一个根结点和若干棵子树构成。树中结点具有相同数据类型及层次关系。
    	Operation
    		InitTree(*T):构造空树T.
    		DestroyTree(*T):销毁树T.
    		CreateTree(*T,definition):按difinition中给出树的定义来构造树。
    		ClearTree(*T):若树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是树的非根结点,则返回它的双亲,否则返回空。
    		LeftChild(T,cur_e):若cur_e是树的非叶结点,则返回它的最左孩子,否则返回空。
    		RightSibling(T,cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空。
    		InsertChild(*T,*p,i,c):其中p指向树的某个结点,i为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p指结点的第i棵子树。
    		DeleteChild(*T,*p,i):其中p指向树T的某个结点,i为所指结点的p的度,操作结果为删除T中p所指结点的第i棵子树。
    endADT
    

存储结构

  1. 双亲表示法

我们假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点在数组中的位置

  1. 孩子表示法

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

  1. 孩子兄弟表示法

任意一棵树,它的节点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。

二叉树

  1. 定义

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

  1. 特点

  2. 特殊二叉树

    1. 斜树:

    所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。

    1. 满二叉树

    在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

    1. 完全二叉树

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

  3. 5个性质

    1. 二叉树性质1

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

    1. 二叉树性质2

    深度为k的二叉树至多有2k-1个结点(k>=1)

    1. 二叉树性质3

    对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1

    1. 二叉树性质4

    具有n个结点的完全二叉树的深度为[log2n] +1([x]表示不大于x的最大整数)

    1. 二叉树性质5

    如果一棵有n个结点的完全二叉树(其深度为[log2n] + 1)的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),对任一结点i(1<=i<=n)有:
    1 . 如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点[i/2]。
    2 . 如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i。
    3 . 如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。

  4. 顺序存储结构
    顺序存储结构一般只用于完全二叉树

  5. 链式存储结构(二叉链表)

    二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。

     typedef struct BiTNode  /* 结点结构 */
     {
        TElemType data;		/* 结点数据 */
        struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
     }BiTNode,*BiTree;
    
  6. 遍历二叉树

    1. 原理

      二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问依次且仅被访问一次

    2. 二叉树遍历的方法

      1. 前序遍历

        规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。ABDGHCEIF

        05 树_第2张图片

      2. 中序遍历

        规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。GDHBAEICF

        05 树_第3张图片

      3. 后序遍历

        规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。GHDBIEFCA

        05 树_第4张图片

    3. 前序遍历算法

       /* 初始条件: 二叉树T存在 */
       /* 操作结果: 前序递归遍历T */
       void PreOrderTraverse(BiTree T)
       { 
       	if(T==NULL)
       		return;
       	printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
       	PreOrderTraverse(T->lchild); /* 再先序遍历左子树 */
       	PreOrderTraverse(T->rchild); /* 最后先序遍历右子树 */
       }
      
    4. 中序遍历算法

       /* 初始条件: 二叉树T存在 */
       /* 操作结果: 中序递归遍历T */
       void InOrderTraverse(BiTree T)
       { 
       	if(T==NULL)
       		return;
       	InOrderTraverse(T->lchild); /* 中序遍历左子树 */
       	printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
       	InOrderTraverse(T->rchild); /* 最后中序遍历右子树 */
       }
      
    5. 后序遍历算法

       /* 初始条件: 二叉树T存在 */
       /* 操作结果: 后序递归遍历T */
       void PostOrderTraverse(BiTree T)
       {
       	if(T==NULL)
       		return;
       	PostOrderTraverse(T->lchild); /* 先后序遍历左子树  */
       	PostOrderTraverse(T->rchild); /* 再后序遍历右子树  */
       	printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
       }
      
    6. 推导遍历结构

      1. 已知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树
      2. 已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树
      3. 注意:已知前序和后序遍历,是不能确定一棵二叉树的。
  7. 二叉树的建立
    和遍历二叉树差不多,以下是使用前序遍历的方式来创建二叉树

     /* 按前序输入二叉树中结点的值(一个字符) */
     /* #表示空树,构造二叉链表表示二叉树T。 */
     void CreateBiTree(BiTree *T)
     { 
     	TElemType ch;
     	
     	/* scanf("%c",&ch); */
     	ch=str[index++];
     
     	if(ch=='#') 
     		*T=NULL;
     	else
     	{
     		*T=(BiTree)malloc(sizeof(BiTNode));
     		if(!*T)
     			exit(OVERFLOW);
     		(*T)->data=ch; /* 生成根结点 */
     		CreateBiTree(&(*T)->lchild); /* 构造左子树 */
     		CreateBiTree(&(*T)->rchild); /* 构造右子树 */
     	}
      }
    
  8. 线索二叉树

    1. 原理

      指向前驱和后继的指针称为线索,加上线索的二叉树链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree),其实线索二叉树,等于是把一棵二叉树变为一个双向链表。

    2. 线索二叉树结构实现

       typedef enum {Link,Thread} PointerTag;	/* Link==0表示指向左右孩子指针, */
       										/* Thread==1表示指向前驱或后继的线索 */
       typedef  struct BiThrNode	/* 二叉线索存储结点结构 */
       {
       	TElemType data;	/* 结点数据 */
       	struct BiThrNode *lchild, *rchild;	/* 左右孩子指针 */
       	PointerTag LTag;
       	PointerTag RTag;		/* 左右标志 */
       } BiThrNode, *BiThrTree;
      
    3. 中序遍历线索化的递归函数代码如下

       BiThrTree pre; /* 全局变量,始终指向刚刚访问过的结点 */
       /* 中序遍历进行中序线索化 */
       void InThreading(BiThrTree p)
       { 
       	if(p)
       	{
       		InThreading(p->lchild); /* 递归左子树线索化 */
       		if(!p->lchild) /* 没有左孩子 */
       		{
       			p->LTag=Thread; /* 前驱线索 */
       			p->lchild=pre; /* 左孩子指针指向前驱 */
       		}
       		if(!pre->rchild) /* 前驱没有右孩子 */
       		{
       			pre->RTag=Thread; /* 后继线索 */
       			pre->rchild=p; /* 前驱右孩子指针指向后继(当前结点p) */
       		}
       		pre=p; /* 保持pre指向p的前驱 */
       		InThreading(p->rchild); /* 递归右子树线索化 */
       	}
       }
      
    4. 遍历代码:

       /* 中序遍历二叉线索树T(头结点)的非递归算法 */
       Status InOrderTraverse_Thr(BiThrTree T)
       { 
       	BiThrTree p;
       	p=T->lchild; /* p指向根结点 */
       	while(p!=T)
       	{ /* 空树或遍历结束时,p==T */
       		while(p->LTag==Link)
       			p=p->lchild;
       		if(!visit(p->data)) /* 访问其左子树为空的结点 */
       			return ERROR;
       		while(p->RTag==Thread&&p->rchild!=T)
       		{
       			p=p->rchild;
       			visit(p->data); /* 访问后继结点 */
       		}
       		p=p->rchild;
       	}
       	return OK;
       }
      
  9. 树、森林与二叉树的转换

    1. 树转为二叉树

      步骤
      1 . 加线。在所有兄弟结点之间加一条连线
      2 . 去线。对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线。
      3 . 层次调整。以树的根结点为轴心,将整棵树顺时针旋转一定角度,使之结构层次分明。注意第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子。

    2. 森林转为二叉树

      步骤
      1 . 把每个树转换成为二叉树
      2 . 第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。

    3. 二叉树转为树

      步骤
      1 . 加线。若某结点的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点。。。。反正就是左孩子的n个右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
      2 . 去线。删除原二叉树中所有结点与其右孩子结点的连线
      3 . 层次调整。使之结构层次分明。

    4. 二叉树转换为森林
      判断一棵二叉树能够转换成一棵树还是森林,标准很简单,那就是只要看这棵二叉树的根结点有没有右孩子,有就是森林,没有就是一个树。

      步骤
      1 . 从根结点开始,若右孩子存在,则把与右孩子结点的连线删除,再查看分离后的二叉树,若右孩子存在,则连线删除。。。,直到所有右孩子连线都删除为止,得到分离的二叉树。
      2 . 再将每棵分离后的二叉树转换为树即可。

下载资源

资源链接

你可能感兴趣的:(数据结构,二叉树,数据结构)