树,二叉树的学习笔记

常用数据逻辑结构:

集合——数据元素间除“同属于一个集合”外,无其它关系

线性结构——一个对一个,如线性表、栈、队列

树形结构——一个对多个,如树

图形结构——多个对多个,如图

树的定义:

树(tree)是由n(n≥0)个结点组成的有限集合T。n=0的树称为空树;对n>0的树,有: (1)仅有一个特殊的结点称为根(root)结点,根结点没有前驱结点; (2)当n>1时,除根结点外其余的结点分为m(m>0)个互不相交的有限集合T1,T2,…,Tm,其中每个集合Ti本身又是一棵树,称之为根的子树( subtree)。

结点(node):由数据元素和构造数据元素之间关系的指针组成

结点的度:结点所拥有的子树的个数

树的度:树中所有结点的度的最大值

叶结点:度为0的结点,也称作终端结点

分支结点:度不为0的结点,除叶结点之外的其余结点。

结点的层次:从根结点到树中某结点所经路径上的分支数

规定树中根结点的层次为1,其余结点的层次等于其双亲结点的层次加1。 若某结点在第l(l≧1)层,则其子结点在第l+1层。

树的深度:树中所有结点的层次的最大值

森林:m(m≥0)棵树的集合

从根结点开始,到达某结点p所经过的所有结点成为结点p的层次路径(有且只有一条)。     结点p的层次路径上的所有结点(p除外)称为p的祖先(ancester) 。     以某一结点为根的子树中的任意结点称为该结点的子孙结点(descent)。

孩子(child)结点:树中一个结点的子树的根结点 双亲(parent)结点:若树中某结点有孩子结点,则这个结点就称作它的孩子结点的双亲结点 兄弟(sibling)结点:具有相同的双亲结点的结点 

无序树:树中任意一个结点的各孩子结点之间的次序构成    无关紧要的树 有序树:树中任意一个结点的各孩子结点有严格排列次序的树

二叉树的性质: 

性质1: 在二叉树的第i层上至多有2i-1个结点;第i层上至少有   1 个结点

性质2: 深度为k的二叉树至多有2k-1个结点;深度为k时至少有    k 个结点

性质3:对于任何一棵二叉树,若2度的结点数有n2个,则叶子数n0必定为n2+1 (即n0=n2+1)

满二叉树:一棵深度为k 且有2k -1个结点的二叉树。(特点:每层都“充满”了结点);如

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

 满二叉树的特点: ◆ 基本特点是每一层上的结点数总是最大结点数。 ◆ 满二叉树的所有的支结点都有左、右子树。 ◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。 完全二叉树(Complete Binary Tree):如果深度为k,由n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应,该二叉树称为完全二叉树。     或深度为k的满二叉树中编号从1到n的前n个结点构成了一棵深度为k的完全二叉树。 其中  2k-1 ≦ n≦2k-1 。

 完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。 完全二叉树的特点:   若完全二叉树的深度为k ,则所有的叶子结点都出现在第k层或k-1层。对于任一结点,如果其右子树的最大层次为l,则其左子树的最大层次为l或l+1。

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

性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2。

 

 

 二叉链表:

typedef struct BiNode{
   TElemType   data;
   struct  BiNode   *lchild,*rchild; //左右孩子指针
}BiNode,*BiTree; 

在n个结点的二叉链表中,有  n+1 个空指针域

分析:必有2n个链域。除根结点外,每个结点有且仅有一个双亲,所以只会有n-1个结点的链域存放指针,指向非空子女结点。空指针数目=2n-(n-1)=n+1

三叉链表:

​
typedef struct TriTNode{  
TelemType data;
 struct TriTNode *lchild,*parent,*rchild; 
}TriTNode,*TriTree;

​

遍历:顺着某一条搜索路径巡访二叉树中的结点,使   得每个结点均被访问一次,而且仅被访问一次

 若规定先左后右,则只有前三种情况:      DLR —— 先(根)序遍历,      LDR —— 中(根)序遍历,      LRD —— 后(根)序遍历。

先序遍历二叉树的操作定义:       若二叉树为空,则空操作;否则       (1)  访问根结点;       (2)  先序遍历左子树;       (3)  先序遍历右子树。

中序遍历二叉树的操作定义:       若二叉树为空,则空操作;否则       (1)  中序遍历左子树;       (2)  访问根结点;       (3)  中序遍历右子树。

 后序遍历二叉树的操作定义:       若二叉树为空,则空操作;否则       (1)  后序遍历左子树;       (2)  后序遍历右子树;         (3)  访问根结点。

 1.先序遍历二叉树:

递归算法 算法的递归定义是:        若二叉树为空,则遍历结束;否则 ⑴ 访问根结点; ⑵ 先序遍历左子树(递归调用本算法); ⑶ 先序遍历右子树(递归调用本算法)。

void  PreorderTraverse(BTNode  *T)
{  if  (T!=NULL) 
{  visit(T->data) ;       /*  访问根结点  */
PreorderTraverse(T->Lchild) ;
PreorderTraverse(T->Rchild) ;     
}
}

非递归算法 设T是指向二叉树根结点的指针变量,非递归算法是: 若二叉树为空,则返回;否则,令p=T; ⑴ 访问p所指向的结点; ⑵ q=p->Rchild ,若q不为空,则q进栈; ⑶ p=p->Lchild ,若p不为空,转(1),否则转(4); ⑷  退栈到p ,转(1),直到栈空为止。

#define  MAX_NODE  50
void  PreorderTraverse( BTNode  *T)
{  BTNode  *Stack[MAX_NODE] ,*p=T, *q ;
int  top=0 ;
if  (T==NULL)  printf(“ Binary Tree is Empty!\n”) ;
else {  do
      {  visit( p-> data ) ;   q=p->Rchild ; 
          if  ( q!=NULL )  stack[++top]=q ;
          p=p->Lchild ; 
          if (p==NULL) { p=stack[top] ;  top-- ; }
      }
   while (p!=NULL) ;
}
}

中序遍历:递归算法 算法的递归定义是:        若二叉树为空,则遍历结束;否则 ⑴ 中序遍历左子树(递归调用本算法); ⑵ 访问根结点; ⑶ 中序遍历右子树(递归调用本算法)。

中序遍历的递归算法
void  InorderTraverse(BTNode  *T)
{  if  (T!=NULL) 
{  InorderTraverse(T->Lchild) ;
visit(T->data) ;       /*   访问根结点   */
InorderTraverse(T->Rchild) ;
}
}

非递归算法 设T是指向二叉树根结点的指针变量,非递归算法是: 若二叉树为空,则返回;否则,令p=T ⑴ 若p不为空,p进栈, p=p->Lchild ; ⑵ 否则(即p为空),退栈到p,访问p所指向的结点; ⑶ p=p->Rchild ,转(1); 直到栈空为止。

#define MAX_NODE  50
void  InorderTraverse( BTNode  *T)
{  BTNode  *Stack[MAX_NODE] ,*p=T ;
    int  top=0 , bool=1 ;
    if  (T==NULL)  printf(“ Binary Tree is Empty!\n”) ;
   else  { do
                 { while (p!=NULL)
                        {  stack[++top]=p ;  p=p->Lchild ;   }
                     if  (top==0)  bool=0 ;
                     else  {  p=stack[top] ;  top-- ;
                                 visit( p->data ) ;  p=p->Rchild ; }
                 }  while (bool!=0) ;
           }
 }

后序遍历:

递归算法 算法的递归定义是:        若二叉树为空,则遍历结束;否则 ⑴ 后序遍历左子树(递归调用本算法); ⑵ 后序遍历右子树(递归调用本算法) ; ⑶ 访问根结点 。

void  PostorderTraverse(BTNode  *T)
{  if  (T!=NULL) 
{  PostorderTraverse(T->Lchild) ;
PostorderTraverse(T->Rchild) ; 
visit(T->data) ;       /*  访问根结点  */ 
}
} 

非递归算法     在后序遍历中,根结点是最后被访问的。因此,在遍历过程中,当搜索指针指向某一根结点时,不能立即访问,而要先遍历其左子树,此时根结点进栈。当其左子树遍历完后再搜索到该根结点时,还是不能访问,还需遍历其右子树。所以,此根结点还需再次进栈,当其右子树遍历完后再退栈到到该根结点时,才能被访问。         因此,设立一个状态标志变量tag :

其次,设两个堆栈S1、S2 ,S1保存结点,S2保存结点的状态标志变量tag 。S1和S2共用一个栈顶指针。       设T是指向根结点的指针变量,非递归算法是: 若二叉树为空,则返回;否则,令p=T; ⑴ 第一次经过根结点p,不访问:      p进栈S1 , tag 赋值0,进栈S2,p=p->Lchild 。 ⑵ 若p不为空,转(1),否则,取状态标志值tag :  ⑶ 若tag=0:对栈S1,不访问,不出栈;修改S2栈顶元素值(tag赋值1) ,取S1栈顶元素的右子树,即p=S1[top]->Rchild ,转(1); ⑷ 若tag=1:S1退栈,访问该结点; 直到栈空为止。

#define MAX_NODE  50
void  PostorderTraverse( BTNode  *T)
{  BTNode  *S1[MAX_NODE] ,*p=T ;
int S2[MAX_NODE] , top=0 , bool=1 ;
if  (T==NULL)  printf(“Binary Tree is Empty!\n”) ;
else  { do
     {   while (p!=NULL)
              {  S1[++top]=p ; S2[top]=0 ; 
                  p=p->Lchild ;   
              }
         if  (top==0)  bool=0 ;
 else if  (S2[top]==0)
                  {  p=S1[top]->Rchild ;  S2[top]=1 ;   }
              else 
                   {  p=S1[top] ;  top-- ;
                       visit( p->data ) ; p=NULL ; 
                        /*  使循环继续进行而不至于死循环 */                       }
}  while (bool!=0) ;
}
}

 层次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。        为保证是按层次遍历,必须设置一个队列,初始化时为空。        设T是指向根结点的指针变量,层次遍历非递归算法是: 若二叉树为空,则返回;否则,令p=T,p入队; ⑴ 队首元素出队到p; ⑵访问p所指向的结点; ⑶将p所指向的结点的左、右子结点依次入队。直到队空为止

#define MAX_NODE  50
void  LevelorderTraverse( BTNode  *T)
{  BTNode  *Queue[MAX_NODE] ,*p=T ;
int  front=0 , rear=0 ;
if  (p!=NULL) 
{  Queue[++rear]=p;    /*   根结点入队  */
while (front<rear)
     {  p=Queue[++front];  visit( p->data );
         if (p->Lchild!=NULL)
               Queue[++rear]=p;    /*   左结点入队  */
         if (p->Rchild!=NULL)
               Queue[++rear]=p;    /*   左结点入队  */
      }
}
}

#define MAX_NODE  50
typedef struct BTNode
{  char  data ;
struct BTNode *Lchild , *Rchild ;
}BTNode ;
BTNode  *Create_BTree(void)   
  /*   建立链式二叉树,返回指向根结点的指针变量  */
{  BTNode  *T , *p , *s[MAX_NODE] ;  
char ch ; int i , j ;
while (1)
{  scanf(“%d”, &i) ;
if  (i==0)  break ;   /*  以编号0作为输入结束  */
else  
     {  ch=getchar() ;
 p=(BTNode *)malloc(sizeof(BTNode)) ;
       p–>data=ch ;
       p–>Lchild=p–>Rchild=NULL ;  s[i]=p ;
       if (i==1)  T=p ; 
       else 
           {  j=i/2 ;    /*    j是i的双亲结点编号  */
                 if (i%2==0)  s[j]->Lchild=p ;
               else  s[j]->Rchild=p ;
            }
   }
}
return(T) ;
}

按先序遍历方式建立         对一棵二叉树进行“扩充”,就可以得到有该二叉树所扩充的二叉树。有两棵二叉树T1及其扩充的二叉树T2如图所示。

 算法:二叉树的扩充方法是:在二叉树中结点的每一个空链域处增加一个扩充的结点(总是叶子结点,用方框“□”表示)。对于二叉树的结点值: ◆ 是char类型:扩充结点值为“?”; ◆ 是int类型:扩充结点值为0或-1;        下面的算法是二叉树的前序创建的递归算法,读入一棵二叉树对应的扩充二叉树的前序遍历的结点值序列。每读入一个结点值就进行分析: ◆ 若是扩充结点值:令根指针为NULL; ◆ 若是(正常)结点值:动态地为根指针分配一个结点,将该值赋给根结点,然后递归地创建根的左子树和右子树。

#define NULLKY  ‘?’
#define MAX_NODE   50
typedef struct BTNode
{  char  data ;
struct BTNode *Lchild , *Rchild ;
}BTNode ;
BTNode  *Preorder_Create_BTree(BTNode  *T)
 /*   建立链式二叉树,返回指向根结点的指针变量  */
{  char ch ; 
ch=getchar() ; getchar(); 
if  (ch==NULLKY) 
{    T=NULL;  return(T) ;  }
else
{  T=(BTNode *)malloc(sizeof(BTNode)) ;
T–>data=ch ;
Preorder_Create_BTree(T->Lchild) ;
Preorder_Create_BTree(T->Rchild) ;
return(T) ; 
}
}

求二叉树的叶子结点数         可以直接利用先序遍历二叉树算法求二叉树的叶子结点数。只要将先序遍历二叉树算法中vist()函数简单地进行修改就可以。

算法实现:

#define  MAX_NODE  50
int  search_leaves( BTNode  *T)
{  BTNode  *Stack[MAX_NODE] ,*p=T;
int  top=0, num=0;
if  (T!=NULL)
{  stack[++top]=p ; 
while (top>0)
   {  p=stack[top--] ;
       if (p->Lchild==NULL&&p->Rchild==NULL)  num++ ;   
        if  (p->Rchild!=NULL )
             stack[++top]=p->Rchild; 
        if  (p->Lchild!=NULL )
                stack[++top]=p->Lchild; 
     }
}
return(num) ;
}

求树的深度:

#define  MAX_NODE  50
int  search_depth( BTNode  *T)
{  BTNode  *Stack[MAX_NODE] ,*p=T;
int  front=0 , rear=0, depth=0, level ;
/*  level总是指向访问层的最后一个结点在队列的位置  */
if  (T!=NULL)
{  Queue[++rear]=p;    /*   根结点入队  */
level=rear ;    /*  根是第1层的最后一个节点  */
while (front<rear)
     {  p=Queue[++front]; 
         if (p->Lchild!=NULL)
               Queue[++rear]=p;    /*   左结点入队  */
         if (p->Rchild!=NULL)
               Queue[++rear]=p;    /*   左结点入队  */
          if (front==level)  
             /*  正访问的是当前层的最后一个结点  */
             {  depth++ ;  level=rear ;  }
      }
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值