树的定义及相关概念

来源:互联网 发布:800源码 编辑:程序博客网 时间:2024/04/28 21:38

 树的定义与表示法
 
树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树,否则它满足如下两个条件:
① 有且仅有一个特定的称为根(Root)的结点; ② 其余的结点可分为m(m≥0)个互不相交的子集T1,T2,…,Tm,其中每个子集本身又是一棵树,并称其为根的子树(Subtree)。             
树的递归定义刻化了树的固有特性,即一棵非空树是由若干棵子树构成的,而子树又可由若干棵更小的子树构成。
  从该定义可知:只有一个结点的树,该结点为根结点;多个结点的树,除根结点之外,它的M棵子树T1,T2,…,Tm也是树,且互不相交。
树的表示法
树形表示法
嵌套集合表示法
凹入表表示法
广义表表示法  树的有关术语
 


度(Degree):
一个结点拥有的子树数称为该结点的度。

树的度:一棵树的度是指该树中结点的最大度数。
 
叶子(Leaf)和分支结点:
度为零的结点称为叶子或终端结点。

度不为零的结点称为分支结点或非终端结点。

除根结点之外的分支结点统称为内部结点,

根结点又称为开始结点。


双亲(Parents)和孩子(Child):
树中某个结点的子树之根称为该结点
的孩子或儿子,相应地,该结点称为孩子的双亲或父亲。


兄弟(Sibling)和堂兄弟:
同一个双亲的孩子称为兄弟。
双亲在同一层的结点互为堂兄弟。

路径(Path):若树中存在一个结点序列
k1,k2,…,kj,使得kj是ki+1的双亲
(1≤i<j),则称该结点序列是从
ki到kj的一条路径或道路。

  若一个结点序列是路径,则在树的树形图表示中,该结点序列“自上而下”地通过路径上的每条边。

祖先(Ancestor)和子孙(Descendant):
一个结点的祖先是指从树的根到该结点所经
分枝上的所有结点(包括根结点)。一个结点的子树的所有结点都称为该结点的子孙。

结点的层数(Level):
是从根起算,设根的层数为1,
其余结点的层数等于其双亲结点
的层数加1。


树的高度(Height):
树中结点的最大层数称
为树的高度或深度(Depth)。

有序树(Ordered Tree)
和无序树(Unordered Tree):
若将树中每个结点的各子树看成
是从左到右有次序的(即不能互换),
则称该树为有序树;否则称为无序树。

森林(Forest):是m(m≥0)棵互不相交
的树的集合。

  对树中每个结点而言,其子树的集合即为森林;反之,给一个森林加上一个结点,使原森林的各棵树成为所加结点的子树,便得到一棵树。


6.2 二叉树的定义
 二叉树的定义
  二叉树(Binary Tree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。
  若二叉树为空集,则称为空二叉树。

二叉树是有序的,它的每个结点至多只有两棵子树,
且有左、右之分,不能互换;
左右子树也可以是空二叉树。


 二叉树的基本形态

  空二叉树
  仅有根结点的二叉树
  右子树为空的二叉树
  左子树为空的二叉树
  左右子树均非空的二叉树
 树与二叉树的区别
  树至少包含一个称为根的结点,而二叉树可以是空二叉树;
  树的结点可有任意有限棵子树(直接后续),而二叉树的任一结点至多只有两棵子树(直接后续);
  树中各子树可以不必区分各子树之间的次序(但有序树规定了左、右排列次序),而二叉树中将两棵子树明确地区分为左子树和右子树,并且当二叉树中一棵子树为空、另一棵子树为非空时,也要明确地指出它们的左、右次序。

二叉树是有序的,它的每个结点至
多只有两棵子树,且有左、右之分,
不能互换;

左右子树也可以是空二叉树。

 


6.2.2 二叉树的性质
二叉树的特殊形态
 
 满二叉树(Full Binary Tree):一棵深度为k且有2k-1个结点的二叉树。

特点1:二叉树的所有分支结点都存在
左子树和右子树

特点2:二叉树的所有叶子结点都在同一层上

 完全二叉树(Complete Binary Tree):深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应。 


将满二叉树删除一些结点得到
从右下角开始,从右到左,从
下到上,删除一些结点

二叉树的性质
 
性质1: 在二叉树的第i层上至多有2^(i-1)个结点
(i≥1)。


性质2: 深度为k的二叉树至多有2^k-1个结点
(k≥1)。

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

性质4: 具有n个结点的完全二叉树的
深度为「log2n」+1

性质5: 如果对一棵有n个结点的完全二叉树
按层序编号(从第1层,每层从左到右,从上到下),

 

则对任一结点i    


(1≤i≤n),有

如果 i=1,则结点i是二叉树的根,无双亲;

如果 i>1,则其双亲PARENT(i)是结点「i/2」。

如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子LCHILD(i)是结点2i。

如果2i+1>n,则结点i无右孩子;否则其右孩子RCHILD(i)是结点2i+1。


typedef char DataType;
typedef struct node{
 DataType data;
 struct node *lchild, *rchild;
}BinTNode;
typedef BinTNode *BinTree;

void main(){
 /*空二叉树,只有指向树根的指针变量,
没有任何结点。*/
 BinTree Tree=NULL;
 CreateBinTree(...);
 PreOrder(Tree);  Tree是指向树根结点的指针
}

 

 void PreOrder(BinTree T){
      if(T){
        printf("%c",T->data);
        PreOrder(T->lchild);
        PreOrder(T->rchild);
      }
  }


6.3 二叉树的遍历
 二叉树遍历的基本概念
 遍历(Traversal):
依次对树中每个结点均做一次且仅做一次访问。


分别称之为前序遍历,中序遍历和后序遍历。

前序遍历
 前序遍历(Preorder Traversal)亦称先序遍历,
定义为:
若二叉树为空,则空操作;
否则,执行下列步骤:
(1)访问根结点
(2)前序遍历左子树
(3)前序遍历右子树

        a       abdche
      /   /  
     b     c
    /     / /
   d     h   e

(1)访问根结点                    a
(2)前序遍历左子树(以b为根)
    (1)访问根结点                b
    (2)前序遍历左子树            d
    (3)前序遍历右子树
(3)前序遍历右子树(以c为根)
    (1)访问根结点                c
    (2)前序遍历左子树            h
    (3)前序遍历右子树            e

        a
      /   /  
     b     c
    / /    / /
   d  !   h   e
  / /    / / / /
 !  !   !  ! ! !
 abd!!!ch!!e!!
     
(1)访问根结点 a           a
(2)前序遍历左子树 b根 
   (1)访问根结点   b      b
   (2)前序遍历左子树   d  d
   (3)前序遍历右子树   ^
(3)前序遍历右子树 c根
   (1)访问根结点   c       c
   (2)前序遍历左子树   h   h
   (3)前序遍历右子树   e   e

             a    前序:abdqxkLmchewy
        /       /   中序:dqbkxmlahcwey
      b          c    后序:qdkmlxbhwyeca
    /    /      /   /
   d      x     h    e
    /    / /        / /
    q   k   L       w   y
           / 
           m 

             a    前序:abdqxklmchewy
        /       /   中序:dqbkxmlahcwey
      b          c    后序:qdkmlxbhwyeca
    /    /      /   /
   d      x     h     e
  / /     / /  / /   / /
 !   q   k   L ! !   w   y
    / / //  / /     / / / /
   !  ! ! ! m  !   !  ! ! !
           / /
           ! !
 前序:abd!q!!xk!!lm!!!ch!!ew!!y!!
(1)访问根结点                        a
(2)前序遍历左子树    以b为根
  (1)访问根结点                      b
  (2)前序遍历左子树                  d
  (3)前序遍历右子树  ^
(3)前序遍历右子树    以c为根
  (1)访问根结点                      c
  (2)前序遍历左子树  以h为根
    (1)访问根结点                    h
    (2)前序遍历左子树  以k为根       k
    (3)前序遍历右子树  以L为根
      (1)访问根结点                  L
      (2)前序遍历左子树  以m为根     m
      (3)前序遍历右子树  ^
  (3)前序遍历右子树  以e为根         e


中序遍历
 中序遍历(Inorder Traversal)定义为:
若二叉树为空,则空操作;
否则,执行下列步骤:
(1)遍历左子树
(2)访问根结点
(3)遍历右子树


        a      
      /   /  
     b     c
    /     / /
   d     h   e
 
  dbahce


 后序遍历

  后序遍历(Postorder Traversal)定义为:
若二叉树为空,则空操作;否则,执行下列步骤:
(1)遍历左子树
(2)遍历右子树
(3)访问根结点 

        a      
      /   /  
     b     c
    /     / /
   d     h   e
 dbheca


构造二叉链表
  基于先序遍历构造二叉链表算法。

度(Degree):一个结点拥有的子树数称为该结点的度。

 

             a    前序:abdqxklmchewy
        /       /   中序:dqbkxmlahcwey
      b          c    后序:qdkmlxbhwyeca
    /    /      /   /
   d      x     h    e
    /    / /        / /
    q   k   L       w   y
           / 
           m 


  简单递归程序写法
  1、写出递归函数的定义:
 函数名,参数,结果(返回值类型和意义)
  2、处理边界情况
  3、按照递归函数的定义
    (利用低一级结果,得到本级的结果),
     自己调用自己,自圆其说

1、递归函数的定义:
 名字:PreOrder,
 参数T:树根结点的地址
 结果:打印前序遍历
2、处理边界情况 : 空树,无打印
 if(T==NULL)return;
3、不是空树
        printf("%c",T->data);  打印根
        PreOrder(T->lchild);  前序打印左子树
        PreOrder(T->rchild);  前序打印右子树
                           +---------
                              整个树的前序
      }
  }

   a    a b !! c !!
  / /
 b   c
/ /  / /
!  ! ! !

      a    a ! b ! !
     / /
    !   b
       / /
       !  !
 
      a     ab!!!
     /  /
     b   !
    / /
   !  !

     a   a!!hjgfhjhj
   /  /
  !    !

  递归程序写法
  1、写出递归函数的定义:
 函数名,参数,结果(返回值类型和意义)
  2、处理边界情况  if(T=NULL) {return 0;printf("你……");}
  3、按照递归函数的定义
    (利用低一级结果,得到本级的结果),
     自己调用自己,自圆其说  ——自圆其说是如何编写调用程序代码,最难在这里~


  1、写出递归函数的定义:
   函数名Height
   参数:T=树根结点的地址
   结果:计算树的高度
  2、处理边界情况:树空
    if(T==NULL)return 0;
    /*边界情况:如果是空树,高度0 */
  3、按照递归函数的定义
    (利用低一级结果,得到本级的结果),
     自己调用自己,自圆其说

    树不空
    左子树高度Height(T->lchild)
    右子树高度Height(T->rchild)
    整个树的高度=max(左子树高度,右子树高度)+1

  int Height(BinTree T){
    int h1,h2;
    /*t是指向根的指针变量
      函数定义:计算t为根的二叉树高度*/
    if(T==NULL)return 0;/*边界情况:如果是空树,高度0 */
    h1=Height(T->lchild);
    h2=Height(T->rchild);
    if(h1>h2)return h1+1;
    else return h2+1;

    /*自圆其说:max{左子树高度,右子树高度}+1*/
  }


  递归程序写法
  1、写出递归函数的定义:
   函数名:NodeCount
   参数:T=树根结点的地址
   结果:计算树的结点数
  2、处理边界情况:空树
   if(T==NULL)return 0;
  3、按照递归函数的定义
    (利用低一级结果,得到本级的结果),
     自己调用自己,自圆其说

   不是空树
    左子树结点数NodeCount(T->lchild)
    右子树结点数NodeCount(T->rchild)
    整个树的结点数=左子树结点数+右子树结点数+1

int NodeCount(BinTree T){
   /*t是指向根的指针变量
     函数定义:计算t为根的二叉树结点数 */
   if(T==NULL)return 0;
   /*边界情况:如果是空树,结点数为0*/
    return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
   /*自圆其说:左子树结点数+右子树结点数+根数1*/
  }


  递归程序写法
  1、写出递归函数的定义:
   函数名:LeafCount
   参数:T=树根结点的地址
   结果:计算树的叶子数
  2、处理边界情况:空树,只有根
   if(T==NULL)return 0;
   if(T->lchild==NULL&&T->rchild==NULL)
    return 1;

  3、按照递归函数的定义
    (利用低一级结果,得到本级的结果),
     自己调用自己,自圆其说

   不是空树
    左子树叶子数LeafCount(T->lchild)
    右子树叶子数LeafCount(T->rchild)
    整个树的叶子数=左子树叶子数+右子树叶子数

  int LeafCount(BinTree T){
   /*t是指向根的指针变量
     函数定义:计算t为根的二叉树叶子结点数*/
   if(T==NULL)return 0;
   /*边界情况:如果是空树,结点数为0*/
   if(T->lchild==NULL&&T->rchild==NULL)return 1;
   /*边界情况:如果只有根,叶子结点数为1*/
   return LeafCount(T->lchild)+LeafCount(T->rchild); 
   /*自圆其说:叶子结点数=左子树叶子结点数+右子树结点数*/
  }


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/SinQiay/archive/2009/05/16/4193611.aspx