zoukankan      html  css  js  c++  java
  • 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

    树、二叉树、三叉树、平衡排序二叉树AVL

    一、树的定义

       树是计算机算法最重要的非线性结构。树中每个数据元素至多有一个直接前驱,但可以有多个直接后继。树是一种以分支关系定义的层次结构。
       a.树是n(≥0)结点组成的有限集合。{N.沃恩}
         (树是n(n≥1)个结点组成的有限集合。{D.E.Knuth})
          在任意一棵非空树中:
            ⑴有且仅有一个没有前驱的结点----根(root)。
            ⑵当n>1时,其余结点有且仅有一个直接前驱。
             ⑶所有结点都可以有0个或多个后继。
       b. 树是n(n≥0)个结点组成的有限集合。
         在任意一棵非空树中:
          ⑴有一个特定的称为根(root)的结点。
          ⑵当n>1时,其余结点分为m(m≥0)个互不相交的子集T1,T2,…,Tm。 每个集合本身又是一棵树,并且称为根的子树(subtree)
         树的固有特性---递归性。即非空树是由若干棵子树组成,而子树又可以由若干棵更小的子树组成。
      树的基本操作
         1、InitTree(&T)           初始化
         2、DestroyTree(&T)    撤消树
         3、CreatTree(&T,F)     按F的定义生成树
         4、ClearTree(&T)       清除
         5、TreeEmpty(T)         判树空
         6、TreeDepth(T)          求树的深度
         7、Root(T)                        返回根结点
         8、Parent(T,x)              返回结点 x 的双亲
         9、Child(T,x,i)              返回结点 x  的第i 个孩子
         10、InsertChild(&T,&p,i,x)  把 x 插入到 P的第i棵子树处
         11、DeleteChild(&T,&p,i)    删除结点P的第i棵子树
         12、traverse(T)            遍历
        树的结点:包含一个数据元素及若干指向子树的分支。
        ●结点的度:    结点拥有子树的数目
        ●叶结点:    度为零的结点
        ●分枝结点:    度非零的结点
        ●树的度:    树中各结点度的最大值
        ●孩子:    树中某个结点的子树的根
        ●双亲:    结点的直接前驱
        ●兄弟:    同一双亲的孩子互称兄弟
        ●祖先:    从根结点到某结点j 路径上的所有结点(不包括指定结点)。
        ●子孙:   某结点的子树中的任一结点称为该结点的子孙
        ●结点层次:   从根结点到某结点 j 路径上结点的数目(包括结点j)
        ●树的深度:    树中结点的最大层次
        ●有向树:结点间的连线是有向的。我们所讲的树都是有向的。
        ●有序树:    若树中结点的各子树从左到右是有次序的,称该树为有序树,否则为无序树
        ●森林:    由 m 棵互不相交的树构成    F=(T1,T2,.......Tm)
         一棵树去掉根结点后就变成了森林。
      二叉树的性质
         二叉树的第i层结点数最多为2^(i-1)个(i>0)。
         深度为K的二叉树至多有(2^k)-1个结点(K>0)。
         对任何一棵二叉树,设n0,n1,n2分别是度为0,1,2的结点数,则有:n0=n2+1
          证明:
          ∵   n= n0+ n1 + n2    (n为结点总数)
                 b= n1 +2 n2        (b 为分支总数)
                 b=n-1       (除根结点外,任一结点都有分支连入父结点)
          ∴   n=b+1= n1 +2 n2 +1= n0+ n1 + n2
                 整理得:     n0 = n2 +1
         具有n个结点的完全二叉树高度为

          

       具有n个结点的完全二叉树具有如下特征:
          ① i=1   根结点,无双亲
              i>1   其双亲结点为 (PARENT(i)= )
         ② 2i>n   结点i无左孩,否则  lchild(i)=2i
         ③ 2i+1>n 结点i无右孩,否则  rchild(i)=2i+1
    二、二叉树三序遍历
    2.1.实验内容

      1.用先序递归遍历法建二叉树
      2.输出三序递归遍历与层次遍历节点访问次序,三序遍历要求使用非递归和递归两种!!!!!!
      3.用先序,中序遍历序列建二叉树
      4.后序遍历复制一棵二叉树,计算叶子个数和树的深度!!!!
      5.输出后序递归遍历及层次遍历结果

    2.2.输入与输出
       输入:输入建立二叉树的先序序列并且带‘#’,用以建树
       输出 :输出四种遍历的序列和用先序中序序列建成的二叉树
    2.3.关键数据结构与算法描述
    关键数据结构:二叉树的结构,节点,左孩子右孩子;栈的数据结构用以采用非递归方式遍历二叉树,循环队列的数据结构用以层次遍历二叉树。

    具体代码如下:

     1 typedef  struct  TreeNode
     2 {
     3     ElemType elem;
     4     struct TreeNode  *LChild,*RChild;
     5 }BiTree,*BinaryTree;            //二叉树数据结构
     6 typedef  struct Queue
     7 {
     8     BinaryTree value[MAXSIZE];
     9     int front,rear;
    10 }LinkQueue;                     //队列数据结构
    11 typedef  BinaryTree  ElemType1; //为BinaryTree起别名
    12 typedef  struct Stack
    13 {
    14     ElemType1 StackElem[MAXSIZE];
    15     int top;
    16 }STACK;                      //栈的数据结构
    View Code

    算法描述:用递归方法进行先序,中序,后序遍历都十分方便与简单,因为利用了树的左右结构。若树空时什么也不做,否则,按照便利的次序依次递归访问即可,如先序遍历:

    //先序递归遍历
    void  PreOrderTraverse(BinaryTree tree)
    {
        if(tree!=NULL)
        {
          visit(tree);                    
          PreOrderTraverse(tree->LChild);
          PreOrderTraverse(tree->RChild);
        }
    }
    View Code

    对于非递归的三序遍历,就需要栈来做数据结构了,对于前序,中序遍历来说,只需要从根节点开始,一直往左走,直至最左边左子树为空,并且在这个过程中,若是先序遍历对于路上的节点都要访问并且入栈直至访问到到最左边的节点,然后退栈访问退栈节点的右子树;若是中序遍历则只需不断的向左走并且入栈但不访问,直至最左边,然后访问最左边节点。然后退栈并访问该节点,用同样的方法访问退栈节点的右子树。最后若栈为空,则访问完成。故此设立一个一直向左走访问并且入栈的函数如下(中序与此类似,暂不赘述):

    /***一直向左走直至获得最左边的指针*************/
    BinaryTree  GofarleftVisit(BinaryTree  tree,STACK  *s)
    {
        if(!tree)
            return NULL;       //若无树直接返回
        BinaryTree  p=tree;
        visit(p);              //先访问逻辑根节点
        while(p->LChild)      
        {
           push(s,p);         //把访问之后的入栈以便访问右子树
           visit(p->LChild);  //访问左子树
           p=p->LChild;       //不断向左移动直至为空
        }
        return  p;
    }
    非递归先序遍历的算法为:
    //用非递归法先序访问
    void  PreOrder(BinaryTree  tree)
    {
        if(!tree)
            return ;
        STACK s;
        InitStack(&s);        
        BinaryTree  p;
        p=GofarleftVisit(tree,&s);   //获得最左指针
        while(p)
        {
            if(p->RChild)
                p=GofarleftVisit(p->RChild,&s); //右边继续向左走
            else
                if(!IsEmptyStack(&s))   
                {
                    pop(&s,p);
                }
                else
                    p=NULL;     //栈空时退出
        }
    }
    View Code

      对于非递归后序遍历,根据其特点,先遍历左子树再遍历右子树,最后访问根节点。则需用两个指针cur和pre来分别标记当前指针和前一个指针的位置。注意压栈时先压根节点,再压右子树,最后左子树。若当前指针cur指向叶子节点则需访问,或者pre指针不为空并且pre指向的节点恰是当前指针指向的左或右节点则代表pre子树的下一层已经无树,并且若等于左指针则右边已无节点,(右边若有则访问完左边之后必然先访问右边而不会跳到头节点);若等于右节点,则左边已访问完或无左边(因右边先压栈)。具体代码如下:

    //非递归后序遍历
    void postOrder(BinaryTree tree)    
    {
        STACK s;
        InitStack(&s);
        BinaryTree  cur,pre=0;
        push(&s,tree);
        /*****用两个指针来判断,如果为叶子节点或者左右子树都访问过就访问该节点****/
        while(!IsEmptyStack(&s))
        {
            cur=gettop(&s);
            if((cur->LChild==NULL&&cur->RChild==NULL)||
               (pre!=NULL&&(pre==cur->RChild||pre==cur->LChild)))
            {   //注意pre只要与一个相等,若为左子树则无右子树;
                //若为右子树则必然访问过左子树或无左子树
                visit(cur);  //如果当前结点为叶子节点或者孩子节点都已被访问就访问
                pop(&s,cur);
                pre=cur;      //标记上次被访问的节点
            }
            else
            {
                if(cur->RChild!=NULL)
                    push(&s,cur->RChild); //注意先把右子树入栈再入左子树,才能保持先访问左子树后访问右子树,后进先出!
                if(cur->LChild!=NULL)    
                    push(&s,cur->LChild);
            }
        }    
    }
    View Code

       接下来是用队列数据结构层次遍历。其实就是迭代的过程,先访问头节点,然后进左儿子,右儿子。每次出队列后都要访问该节点,然后再看该节点是否有左右子树若有则按照先左后右的顺序进队排在队尾等待遍历然后不断地循环迭代直至队为空则遍历结束。很容易理解!
    具体代码如下:

    //队列进行的二叉树层次遍历
    void HierarchyBiTree(BinaryTree tree)
    {
            LinkQueue Q;  //注意此处不能是指针
            InitQueue(&Q);
            BinaryTree  p=tree;  
            if (tree==NULL)
                return ;  
            visit(p);     
            if (p->LChild)
               EnQueue(&Q,&p->LChild);  //若指针不空则入队列
            if (p->RChild)
               EnQueue(&Q, &p->RChild); //若指针不空则入队列
            while (!IsEmpty(&Q))      
            {                
                 DeQueue(&Q, &p);       //弹出指针进行访问
                 visit(p);        
                 if (p->LChild)          
                    EnQueue(&Q, &p->LChild);  //对指针所指的结构进行判断若左右子树不空
                 if (p->RChild)
                    EnQueue(&Q, &p->RChild);  //则先进左子树,后进右子树,以保证从左到右遍历
            }
    }
    View Code

       然后关于二叉树的复制,先复制左子树,然后复制右子树,最后复制头节点;在复制左右子树的时候同时也是复制树,故可用递归实现。同理,关于求叶子节点,求树的深度也是如此用递归即可实现,复制树的具体代码如下:

    /************后序遍历复制一棵二叉树*************/
    void  CopyBiTree(BinaryTree  tree,BinaryTree &newtree)
    {
        BinaryTree  lchild,rchild;
        if(!tree)
        {
            newtree=NULL;
        }
        if(tree->LChild)//若左子树存在则递归复制
        {
            CopyBiTree(tree->LChild,lchild);
        }
        else
        {
            lchild=NULL; //否则为零
        }
        if(tree->RChild)
        {
            CopyBiTree(tree->RChild,rchild);
        }
        else
        {
            rchild=NULL;
        }
        newtree=(BinaryTree)malloc(sizeof(BiTree));
        newtree->elem=tree->elem;
        newtree->LChild=lchild;  
        newtree->RChild=rchild;
    }
    View Code

       最后就是根据先序和中序序列建树,有一定的难度需要用到递归和分治法的一些知识。首先可以证明用先序,中序遍历序列是可以还原二叉树的,因为根据先序序列可以很清楚的知道二叉树的根节点就是第一个元素,然后以这个节点把中序序列分成两半,在这个节点左边的必是左子树(因为是中序序列),而在其右边的是右子树,而左子树右子树有是一个树,故可以在更小的范围内找到左子树的根节点,在以该节点为分界点,在更小的范围内查找下去,右子树也是如此。在递归的过程中要注意进行比较的两个序列长度必然要相等,递归终止的条件就是左边分到不能再比,右边也向右不能再比为止。这样也就在递归中建立了二叉树。具体代码如下:

    //首先得到中序序列二分后左边的长度
    int get_left_len(int rootpos,int in_begin,int in_end,char * pre_order,char * in_order )
    {
          for(int i = in_begin; i <= in_end; i++)
          {
             if(in_order[i] == pre_order[rootpos])
             {
                  return i-in_begin;  //以双亲节点为分界点划分,返回左边的真实长度
             }
          }
          return -1;                 //若没有则返回负值,用于判断
    }
    
    void creat(BinaryTree *pnode,int pre_begin,int pre_end,int in_begin,int in_end,
               char * pre_order,char * in_order)
    {
        *pnode =(BinaryTree)malloc(sizeof(BiTree)); //申请空间
        BinaryTree temp = *pnode;                   //创建遍历指针
        temp->elem = pre_order[pre_begin];          //开始必为根节点
        temp->LChild = NULL;                        //一定要初始化为0
        temp->RChild = NULL;
        if(pre_begin == pre_end)
        {
           return ;             //只有一个节点,则已创建完毕
        }
        int left_len = get_left_len(pre_begin,in_begin,in_end,pre_order,in_order);
        if(left_len > 0)     //若没有会返回-1;若为0,则上面已创建;否则创建左子树
        {
          creat(&temp->LChild,pre_begin+1,pre_begin+left_len,
                in_begin,in_begin+left_len-1,pre_order,in_order);
        }
        if(left_len < (in_end - in_begin)) //若left_len+inbegin>in_end-1则已经结束,否则创建右子树
        {
            creat(&temp->RChild,pre_begin+left_len+1,pre_end,
                   in_begin+left_len+1,in_end,pre_order,in_order);
        }
    }
    View Code

    2.4.测试与理论
     具体的测试与理论见下图
    测试数据一:

    先序遍历:ABDFCEG
    中序遍历:DFBAECG
    后序遍历:FDBEGCA
    输入数据:  ABD#F###CE##G##
    对于测试先序和中序序列建树的序列为
    char * pre_order = "ABDEGJCFHIK";//先序序列
    char * in_order = "DBGJEACFIKH"; //中序序列
    输出结果截图:

    测试数据二:

     

    先序遍历:ABDEGJCFHIK
    中序遍历:DBGJEACFIKH
    后序遍历:DJGEBKIHFCA
    输入序列:ABD##EG#J###C#F#HI#K###

    输出结果见下图:

    2.5.附录

      1 #include "stdio.h"
      2 #include "stdlib.h"
      3 #include "iostream"
      4 using  namespace std;
      5 #define  MAXSIZE   100
      6 #define   OK        1
      7 #define   NO        0
      8 /**********************************************/
      9 typedef   int    status;
     10 typedef   char   ElemType;
     11 typedef  struct  TreeNode
     12 {
     13     ElemType elem;
     14     struct TreeNode  *LChild,*RChild;
     15 }BiTree,*BinaryTree;            //二叉树数据结构
     16 typedef  struct Queue
     17 {
     18     BinaryTree value[MAXSIZE];
     19     int front,rear;
     20 }LinkQueue;                     //队列数据结构
     21 typedef  BinaryTree  ElemType1; //为BinaryTree起别名
     22 typedef  struct Stack
     23 {
     24     ElemType1 StackElem[MAXSIZE];
     25     int top;
     26 }STACK;                         //栈的数据结构
     27 /************************************************/
     28 /*************以下是循环队列的定义**************/
     29 void  InitQueue( LinkQueue  *q)
     30 {
     31     q->front=-1;       //注意初始化为-1
     32     q->rear=-1;
     33 }
     34 status  IsEmpty(LinkQueue *q)
     35 {
     36     if(q->rear==q->front)
     37         return  OK;       //循环队列开始为空或者运行时出队的光标指到入队的光标
     38     else
     39         return NO;
     40 }
     41 status IsFull(LinkQueue *q)
     42 {
     43     if(q->front==(q->rear+1)%MAXSIZE)
     44         return  OK;      //队满的标志就是q->front指向哑元且哑元左边为q->rear
     45     else
     46         return NO;
     47 }
     48 void EnQueue(LinkQueue *q, BinaryTree *tree)
     49 {
     50     if(IsFull(q))
     51         return ;                     //入队操作,若队满则不能入队
     52     q->rear=(++(q->rear))%MAXSIZE;   //注意一定要先自加,再赋值
     53     q->value[q->rear]=*tree;
     54 }
     55 void DeQueue(LinkQueue *q, BinaryTree *tree)
     56 {
     57     if(IsEmpty(q))
     58         return ;
     59     q->front=(++q->front)%MAXSIZE;
     60     *tree=q->value[q->front];  //注意tree是指向指针的指针,不然将出错    
     61 }
     62 /**************************************************************/
     63 /******************以下是栈的定义******************************/
     64 void  InitStack(STACK  *s)
     65 {
     66     s->top=-1;          //初始化
     67 }
     68 void  push(STACK  *s,ElemType1  e)
     69 {
     70     if(s->top>=MAXSIZE-1)
     71         return ;
     72     s->StackElem[++s->top]=e;     //压栈
     73 }
     74 void pop(STACK  *s,ElemType1 &e)
     75 {
     76     if(s->top<=-1)
     77         return ;
     78     e=s->StackElem[s->top];       //出栈
     79     s->top--;
     80 }
     81 ElemType1   gettop(STACK  *s)
     82 {
     83     return s->StackElem[s->top];  //获得栈顶元素
     84 }
     85 status  IsEmptyStack(STACK *s)    //判断是否栈空
     86 {
     87     if(s->top==-1)
     88         return OK;
     89     else
     90         return  NO;
     91 }
     92 /******************************************************************/
     93 /***************递归创建二叉树,要求读入先序序列和‘#’****************/
     94 BinaryTree CreatTree(BinaryTree tree)
     95 {
     96     char ch;
     97     if((ch=getchar())=='#')
     98         tree=NULL;
     99     else
    100     {
    101         tree=(BinaryTree)malloc(sizeof(BiTree));
    102         tree->elem=ch;
    103         tree->LChild=CreatTree(tree->LChild);
    104         tree->RChild=CreatTree(tree->RChild);
    105     }
    106     return tree;
    107 }
    108 //最简单的访问二叉树
    109 void  visit(BinaryTree  tree)
    110 {
    111     printf("%c  ",tree->elem);
    112 }
    113 /**************以下是四种对二叉树的遍历方法***********************/
    114 //先序递归遍历
    115 void  PreOrderTraverse(BinaryTree tree)
    116 {
    117     if(tree!=NULL)
    118     {
    119       visit(tree);                    
    120       PreOrderTraverse(tree->LChild);
    121       PreOrderTraverse(tree->RChild);
    122     }
    123 }
    124 
    125 /***一直向左走直至获得最左边的指针*************/
    126 BinaryTree  GofarleftVisit(BinaryTree  tree,STACK  *s)
    127 {
    128     if(!tree)
    129         return NULL;       //若无树直接返回
    130     BinaryTree  p=tree;
    131     visit(p);              //先访问逻辑根节点
    132     while(p->LChild)      
    133     {
    134        push(s,p);         //把访问之后的入栈以便访问右子树
    135        visit(p->LChild);  //访问左子树
    136        p=p->LChild;       //不断向左移动直至为空
    137     }
    138     return  p;
    139 }
    140 //用非递归法先序访问
    141 void  PreOrder(BinaryTree  tree)
    142 {
    143     if(!tree)
    144         return ;
    145     STACK s;
    146     InitStack(&s);        
    147     BinaryTree  p;
    148     p=GofarleftVisit(tree,&s);   //获得最左指针
    149     while(p)
    150     {
    151         if(p->RChild)
    152             p=GofarleftVisit(p->RChild,&s); //右边继续向左走
    153         else
    154             if(!IsEmptyStack(&s))   
    155             {
    156                 pop(&s,p);
    157             }
    158             else
    159                 p=NULL;     //栈空时退出
    160     }
    161 }
    162 
    163 //中序递归遍历 
    164 void  InOrderTraverse(BinaryTree tree)
    165 {
    166     if(tree!=NULL)
    167     {
    168      InOrderTraverse(tree->LChild);
    169      visit(tree);
    170      InOrderTraverse(tree->RChild);
    171     }
    172 }
    173 //中序非递归遍历二叉树
    174 BinaryTree  gofarleft(BinaryTree  tree,STACK  *s)
    175 {
    176     if(!tree)
    177         return NULL;
    178     BinaryTree  p=tree;
    179     while(p->LChild) //一直向左走,不断入栈
    180     {
    181        push(s,p);      
    182        p=p->LChild;
    183     }
    184     return  p;
    185 }
    186 void  InOrder(BinaryTree  tree)
    187 {
    188     if(!tree)
    189         return ;
    190     STACK s;
    191     InitStack(&s);
    192     BinaryTree  p;
    193     p=gofarleft(tree,&s);
    194     while(p)
    195     {
    196         visit(p);     //先访问最左元素
    197         if(p->RChild)
    198             p=gofarleft(p->RChild,&s);
    199         else
    200             if(!IsEmptyStack(&s))
    201             {
    202                 pop(&s,p);      //向上追溯
    203             }
    204             else
    205                 p=NULL;     //栈空时恰访问完
    206     }
    207 }
    208 /************************************/
    209 
    210 //后序递归遍历
    211 void  PostOrderTraverse(BinaryTree tree)
    212 {
    213     if(tree!=NULL)
    214     {
    215      PostOrderTraverse(tree->LChild);
    216      PostOrderTraverse(tree->RChild);
    217         visit(tree);
    218     }
    219 }
    220  //非递归后序遍历
    221 void postOrder(BinaryTree tree)    
    222 {
    223     STACK s;
    224     InitStack(&s);
    225     BinaryTree  cur,pre=0;
    226     push(&s,tree);
    227     /*****用两个指针来判断,如果为叶子节点或者左右子树都访问过就访问该节点****/
    228     while(!IsEmptyStack(&s))
    229     {
    230         cur=gettop(&s);
    231         if((cur->LChild==NULL&&cur->RChild==NULL)||
    232            (pre!=NULL&&(pre==cur->RChild||pre==cur->LChild)))
    233         {   //注意pre只要与一个相等,若为左子树则无右子树;
    234             //若为右子树则必然访问过左子树或无左子树
    235             visit(cur);  //如果当前结点为叶子节点或者孩子节点都已被访问就访问 
    236             pop(&s,cur);
    237             pre=cur;      //标记上次被访问的节点 
    238         }
    239         else
    240         {
    241             if(cur->RChild!=NULL)
    242                 push(&s,cur->RChild); //注意先把右子树入栈再入左子树,才能保持先访问左子树后访问右子树
    243             if(cur->LChild!=NULL)    
    244                 push(&s,cur->LChild);
    245         }
    246     }    
    247 }
    248 /******************************************************/
    249 //队列进行的二叉树层次遍历
    250 void HierarchyBiTree(BinaryTree tree)
    251 {
    252         LinkQueue Q;  //注意此处不能是指针
    253         InitQueue(&Q);
    254         BinaryTree  p=tree;  
    255         if (tree==NULL)
    256             return ;  
    257         visit(p);     
    258         if (p->LChild)
    259            EnQueue(&Q,&p->LChild);  //若指针不空则入队列
    260         if (p->RChild)
    261            EnQueue(&Q, &p->RChild); //若指针不空则入队列
    262         while (!IsEmpty(&Q))      
    263         {                
    264              DeQueue(&Q, &p);       //弹出指针进行访问
    265              visit(p);        
    266              if (p->LChild)          
    267                 EnQueue(&Q, &p->LChild);  //对指针所指的结构进行判断若左右子树不空
    268              if (p->RChild)
    269                 EnQueue(&Q, &p->RChild);  //则先进左子树,后进右子树,以保证从左到右遍历
    270         }
    271 }
    272 /***************************************************/
    273 /********计算叶子节点数*************/
    274 void   CountLeaf(BinaryTree  tree,int  &count)
    275 {
    276     if(tree)
    277     {
    278       if((tree->LChild==NULL)&&(tree->RChild==NULL))
    279         count++;
    280       CountLeaf(tree->LChild,count);
    281       CountLeaf(tree->RChild,count);
    282     }
    283 }
    284 /************计算树的深度**************/
    285 int  TreeDepth(BinaryTree  tree)
    286 {
    287     int  depth,ldepth,rdepth;
    288     if(!tree)
    289         depth=0;
    290     else
    291     {
    292         ldepth=TreeDepth(tree->LChild);
    293         rdepth=TreeDepth(tree->RChild);
    294         depth=(ldepth>rdepth ? ldepth:rdepth)+1;    
    295     }
    296     return  depth;
    297 }
    298 /************后序遍历复制一棵二叉树*************/
    299 void  CopyBiTree(BinaryTree  tree,BinaryTree &newtree)
    300 {
    301     BinaryTree  lchild,rchild;
    302     if(!tree)
    303     {
    304         newtree=NULL;
    305     }
    306     if(tree->LChild)//若左子树存在则递归复制
    307     {
    308         CopyBiTree(tree->LChild,lchild);
    309     }
    310     else
    311     {
    312         lchild=NULL; //否则为零
    313     }
    314     if(tree->RChild)
    315     {
    316         CopyBiTree(tree->RChild,rchild);
    317     }
    318     else
    319     {
    320         rchild=NULL;
    321     }
    322     newtree=(BinaryTree)malloc(sizeof(BiTree));
    323     newtree->elem=tree->elem;
    324     newtree->LChild=lchild;  
    325     newtree->RChild=rchild;
    326 }
    327 /*****************************************************/        
    328 /*************根据先序和中序序列建二叉树*******************/
    329 //首先得到中序序列二分后左边的长度
    330 int get_left_len(int rootpos,int in_begin,int in_end,char * pre_order,char * in_order )
    331 {
    332       for(int i = in_begin; i <= in_end; i++)
    333       {
    334          if(in_order[i] == pre_order[rootpos])
    335          {
    336               return i-in_begin;  //以双亲节点为分界点划分,返回左边的真实长度
    337          }
    338       }
    339       return -1;                 //若没有则返回负值,用于判断
    340 }
    341 
    342 void creat(BinaryTree *pnode,int pre_begin,int pre_end,int in_begin,int in_end,
    343            char * pre_order,char * in_order)
    344 {
    345     *pnode =(BinaryTree)malloc(sizeof(BiTree)); //申请空间
    346     BinaryTree temp = *pnode;                   //创建遍历指针
    347     temp->elem = pre_order[pre_begin];          //开始必为根节点
    348     temp->LChild = NULL;                        //一定要初始化为0
    349     temp->RChild = NULL;
    350     if(pre_begin == pre_end)
    351     {
    352        return ;             //只有一个节点,则已创建完毕
    353     }
    354     int left_len = get_left_len(pre_begin,in_begin,in_end,pre_order,in_order);
    355     if(left_len > 0)     //若没有会返回-1;若为0,则已创建;否则创建左子树
    356     {
    357       creat(&temp->LChild,pre_begin+1,pre_begin+left_len,
    358             in_begin,in_begin+left_len-1,pre_order,in_order);
    359     }
    360     if(left_len < (in_end - in_begin)) //若left_len+inbegin>in_end-1则已经结束,否则创建右子树
    361     {
    362         creat(&temp->RChild,pre_begin+left_len+1,pre_end,
    363                in_begin+left_len+1,in_end,pre_order,in_order);
    364     }
    365 }
    366 /*********************************************************/
    367 void MainMenu(  )
    368 {
    369     BinaryTree tree=0,newtree;
    370     int count=0,depth;
    371     /**********display***********/
    372     tree=CreatTree(tree);
    373     printf("前序遍历:
    ");
    374     PreOrderTraverse(tree);
    375     printf("
    中序遍历:
    ");
    376     InOrderTraverse(tree);
    377     printf("
    后序遍历:
    ");
    378     PostOrderTraverse(tree);
    379     printf("
    层次遍历二叉树:
    ");
    380     HierarchyBiTree(tree); 
    381     printf("
    非递归先序遍历
    ");
    382     PreOrder(tree);
    383     printf("
    非递归中序遍历
    ");
    384     InOrder(tree);
    385     printf("
    非递归后序遍历
    ");
    386     postOrder(tree);
    387     /********algorithm************/
    388     CountLeaf(tree,count);
    389     printf("
    叶子个数为:%d
    ",count);
    390     depth=TreeDepth(tree);
    391     printf("
    树的深度为:%d
    ",depth);
    392     printf("
    复制二叉树后的结果:
    ");
    393     CopyBiTree(tree,newtree);
    394     printf("
    先序遍历:
    ");
    395     PreOrderTraverse(newtree);
    396     printf("
    中序遍历:
    ");
    397     InOrderTraverse(newtree);
    398     printf("
    后序遍历:
    ");
    399     PostOrderTraverse(newtree);
    400     printf("
    层次遍历二叉树:
    ");
    401     HierarchyBiTree(newtree); 
    402     /*********用先序和中序建树并输出*************/
    403     char * pre_order = "ABDEGJCFHIK";//先序序列
    404     char * in_order = "DBGJEACFIKH"; //中序序列
    405     BinaryTree root = NULL;
    406     creat(&root,0,strlen(pre_order)-1,0,strlen(in_order)-1,pre_order,in_order);
    407     printf("用先序和中序建树后的结果:
    ");
    408     printf("
    后序遍历:
    ");
    409     PostOrderTraverse(root);
    410     printf("
    层次遍历二叉树:
    ");
    411     HierarchyBiTree(root); 
    412     printf("
    操作结束
    ");
    413 }
    414 
    415 /* 测试数据 ABD#F###CE##G##           */
    416 /* 测试数据 ABD##EG#J###C#F#HI#K###   */ 
    417 int main()
    418 {
    419     MainMenu();
    420     return 0;
    421 }
    422     
    View Code

    三、三叉树——带双亲指针的二叉树非递归遍历
    3.1.实验内容
       建立三叉链表存储结构,实现三序非递归遍历。
    3.2.输入与输出
       输入:带有“#”的二叉树字符串。
       输出:三序遍历的结果。
    3.3.关键数据结构与算法描述
    关键数据结构:
      三叉链表的数据结构如下:
    typedef  char ElemType;
    typedef  struct TriTree
    {
        ElemType elem;
        struct TriTree *lchild,*rchild,*parent;
    }*TRITREE,TriTreeNode;
    算法描述:
       三叉链表的创建和二叉树创建基本相同,只不过增加了双亲指针就要给其赋值,根节点的双亲为NULL,其他节点在先序递归遍历建二叉树的时候赋值,若该节点有左右子树,则左右节点的双亲指针就指向该节点。具体代码为:

    if(tree->lchild)
    {
      tree->lchild->parent=tree;//指向双亲节点
    }
    if(tree->rchild)
    {
      tree->rchild->parent=tree;//指向双亲节点
    }
    View Code

    然后是三序遍历,1.首先是先序非递归遍历,先遍历头节点,再遍历左子树,然后是右子树,注意到从此处的双亲指针的用法,可以回溯。因此,当树不为空的时候,首先遍历该节点,然后看是否有左子树,若有则指向左子树,若无左子树,则看是否有右子树,若有则指向右子树。如果左右子树都不存在,则是叶子节点需要回溯。选定一个“记录指针p”,永远指向遍历指针刚刚走过的位置。而遍历指针则回溯。若遍历指针的左儿子为p,并且右儿子不空则遍历右子树;若回溯到根节点的双亲则结束。否则继续回溯。直至两种情况出现一种。一直循环就可实现先序遍历。代码如下:

     1 //非递归先序遍历三叉链表
     2 void   preorder(TRITREE  tree)
     3 {
     4     TRITREE p;
     5     while(tree)      //有树就循环
     6     {
     7         visit(tree);   //访问根节点
     8         if(tree->lchild)
     9         {
    10             tree=tree->lchild ;  //一定要先看左子树再看右子树
    11         }
    12         else  if(tree->rchild )
    13         {
    14             tree=tree->rchild ; //进入下一循环
    15         }
    16         else    
    17             while(1)      
    18             {
    19                     p=tree;
    20                     tree=tree->parent;//形成连接结构
    21                     if(!tree)
    22                         break;        //若无树则退出
    23                     if(tree->lchild == p&&tree->rchild )
    24                     {   
    25                         tree=tree->rchild ; //访问完左子树,访问右子树
    26                         break;
    27                     }
    28    
    29             }
    30     }
    31 }
    View Code

    2.中序遍历思想是先遍历左子树再遍历头节点,最后遍历右子树。因回溯时有左子树和右子树两种情况,则用一个标量来记录mark=0代表未遍历左子树,mark=1代表已遍历左子树。则当树不空时开始循环,mark开始置为零。要是没遍历左子树则要先遍历左子树。左子树遍历之后mark置为一。然后看以该节点为根的右子树是否存在若存在则遍历,若不存在则回溯,同样设p跟随遍历节点,若是左边回溯则遍历该节点,若是右边回溯则继续向上推进,若推进到最上面则遍历结束。具体代码如下:

     1 //非递归中序遍历三叉链表
     2 void   inorder(TRITREE  tree)
     3 {
     4     TRITREE p;
     5     int  mark=0;//表示左子树未遍历
     6     while(tree)
     7     {
     8         if(mark==0)
     9         {
    10             if(tree->lchild)
    11             {
    12                 tree=tree->lchild;//一直到最左边的节点
    13             }
    14             else
    15             {
    16                mark=1;  //然后标记左边已遍历,其实在下面遍历
    17             }
    18         }
    19         else
    20         {
    21             visit(tree);  //遍历标记节点
    22             if(tree->rchild)
    23             {
    24                 tree=tree->rchild;//若有右子树,则移位
    25                 mark=0;           //标记未遍历,回到上步
    26             }
    27                  else
    28             { //若无右子树,则回溯
    29               while(1) 
    30               {
    31                     p=tree;
    32                     tree=tree->parent;
    33                     if(!tree)
    34                         break;
    35                     if(tree->lchild == p)
    36                     {   
    37                         mark=1;//表示左孩子遍历过
    38                         break;
    39                     }
    40               }
    41    
    42             }
    43         }
    44     }
    45 }            
    View Code

    3.后序遍历需要设定一个标量flag分别表示(0):左子树未遍历(1):左子树已遍历,该遍历右子树;(2)右子树已遍历,应遍历头节点。则开始flag=0;开始遍历遍历完左子树flag=1;开始遍历右子树,此时若右子树还是棵树则要flag=0;判断这棵树的左右子树情况直至右边也被遍历则要遍历头节点置flag=2;开始访问。访问完后要进行回溯,若是从左边过来的就要访问右边,若是从右边过来的则要访问该节点。一直循环就可得到结果,具体代码如下:

    //非递归后序遍历三叉链表
    void  postorder(TRITREE  tree)
    {
        int  flag=0;//标志变量可取0,1,2三种状态
        TRITREE  p;
        while(tree)
        {
            switch(flag)
            {
            case 0://左子树未遍历
                if(tree->lchild)
                    tree=tree->lchild;
                else
                    flag=1;
                break;
            case 1://右子树未遍历
                if(tree->rchild)
                {
                    tree=tree->rchild;
                    flag=0; //右子树可能是一棵树,重新遍历树的左孩子
                }
                else
                {
                    flag=2; //没有右子树则开始遍历头节点
                }
                break;
            case 2: //开始遍历头节点
                visit(tree);
                p=tree;
                tree=tree->parent; //回溯判断
                if(tree)
                {
                    if(p==tree->lchild)
                    {
                      flag=1;//左孩子已遍历,开始右子树
                    }
                   else
                   {
                      flag=2;//右孩子已遍历,开始遍历头节点
                   }
                }
                break;
            }
        }
    }
    View Code

     3.4.测试与理论
     测试数据:

    先序遍历:ABDFCEG
    中序遍历:DFBAECG
    后序遍历:FDBEGCA
    输入数据:  ABD#F###CE##G##

    结果见下图显示:

    3.5.附录

      1 #include "stdio.h"
      2 #include "iostream.h"
      3 #include "stdlib.h"
      4 typedef  char ElemType;
      5 typedef  struct TriTree
      6 {
      7     ElemType elem;
      8     struct TriTree *lchild,*rchild,*parent;
      9 }*TRITREE,TriTreeNode;
     10 
     11 
     12 //先序遍历创建三叉链表
     13 TRITREE CreatTree(TRITREE &tree)
     14 {
     15     char ch;
     16     if((ch=getchar())=='#')
     17         tree=NULL;
     18     else
     19     {
     20         tree=(TRITREE)malloc(sizeof(TriTreeNode));
     21         tree->elem=ch;
     22         tree->lchild=CreatTree(tree->lchild);
     23         tree->rchild=CreatTree(tree->rchild);
     24         //增加parent指针,若无左右孩子则不用赋值
     25         if(tree->lchild)
     26         {
     27             tree->lchild->parent=tree;//指向双亲节点
     28         }
     29     
     30         if(tree->rchild)
     31         {
     32             tree->rchild->parent=tree;//指向双亲节点
     33         }
     34     }
     35     return tree;
     36 }
     37 //最简单的访问二叉树
     38 void  visit(TRITREE  tree)
     39 {
     40     printf("%c  ",tree->elem);
     41     
     42 }
     43 //非递归先序遍历三叉链表
     44 void   preorder(TRITREE  tree)
     45 {
     46     TRITREE p;
     47     while(tree)      //有树就循环
     48     {
     49         visit(tree);   //访问根节点
     50         if(tree->lchild)
     51         {
     52             tree=tree->lchild ;  //一定要先看左子树再看右子树
     53         }
     54         else  if(tree->rchild )
     55         {
     56             tree=tree->rchild ; //进入下一循环
     57         }
     58         else     
     59             while(1)       
     60             {
     61                     p=tree;
     62                     tree=tree->parent;//形成连接结构
     63                     if(!tree)
     64                         break;        //若无树则退出
     65                     if(tree->lchild == p&&tree->rchild )
     66                     {    
     67                         tree=tree->rchild ; //访问完左子树,访问右子树
     68                         break;
     69                     }
     70     
     71             }
     72     }
     73 }
     74 //非递归中序遍历三叉链表
     75 void   inorder(TRITREE  tree)
     76 {
     77     TRITREE p;
     78     int  mark=0;//表示左子树未遍历
     79     while(tree)
     80     {
     81         if(mark==0)
     82         {
     83             if(tree->lchild)
     84             {
     85                 tree=tree->lchild;//一直到最左边的节点
     86             }
     87             else
     88             {
     89                mark=1;  //然后标记左边已遍历,其实在下面遍历
     90             }
     91         }
     92         else
     93         {
     94             visit(tree);  //遍历标记节点
     95             if(tree->rchild)
     96             {
     97                 tree=tree->rchild;//若有右子树,则移位
     98                 mark=0;           //标记未遍历,回到上步
     99             }
    100             else
    101             { //若无右子树,则回溯
    102               while(1)  
    103               {
    104                     p=tree;
    105                     tree=tree->parent;
    106                     if(!tree)
    107                         break;
    108                     if(tree->lchild == p)
    109                     {    
    110                         mark=1;//表示左孩子遍历过
    111                         break;
    112                     }
    113               }
    114     
    115             }
    116         }
    117     }
    118 }            
    119 
    120 //非递归后序遍历三叉链表
    121 void  postorder(TRITREE  tree)
    122 {
    123     int  flag=0;//标志变量可取0,1,2三种状态
    124     TRITREE  p;
    125     while(tree)
    126     {
    127         switch(flag)
    128         {
    129         case 0://左子树未遍历
    130             if(tree->lchild)
    131                 tree=tree->lchild;
    132             else
    133                 flag=1;
    134             break;
    135         case 1://右子树未遍历
    136             if(tree->rchild)
    137             {
    138                 tree=tree->rchild;
    139                 flag=0; //右子树可能是一棵树,重新遍历树的左孩子
    140             }
    141             else
    142             {
    143                 flag=2; //没有右子树则开始遍历头节点
    144             }
    145             break;
    146         case 2: //开始遍历头节点
    147             visit(tree);
    148             p=tree;
    149             tree=tree->parent; //回溯判断
    150             if(tree)
    151             {
    152                 if(p==tree->lchild)
    153                 {
    154                   flag=1;//左孩子已遍历,开始右子树
    155                 }
    156                else
    157                {
    158                   flag=2;//右孩子已遍历,开始遍历头节点
    159                }
    160             }
    161             break;
    162         }
    163     }
    164 }
    165             
    166 //abd#f###ce##g##
    167 int  main()
    168 {
    169     TRITREE  tree;
    170     
    171     tree=CreatTree(tree);
    172     tree->parent=0;
    173     cout<<endl<<"先序非递归遍历三叉树:"<<endl;
    174     preorder(tree);
    175     cout<<endl<<"中序非递归遍历三叉树:"<<endl;
    176     inorder(tree);
    177     cout<<endl<<"后序非递归遍历三叉树:"<<endl;
    178     postorder(tree);
    179     cout<<endl;
    180     return 0;
    181 }
    View Code

    四、平衡排序二叉树AVL
    4.1.实验内容
          建立排序平衡二叉树。
    4.2.输入与输出
      输入:输入一组节点,从而建立平衡排序二叉树。
      输出:输出平衡二叉树的先序,中序遍历,以及每个节点的平衡因子,以便进行还原二叉树的形状,判断算法是否正确。
    4.3.关键数据结构与核心算法
       关键数据结构:
       因为是二叉树,则要有基本的节点,左右孩子指针,因为要排序旋转,则要知道平衡因子,注意此处可以根据需要来添加双亲指针,因为运用了引用,则省去了此处。因此数据结构为:
    typedef struct BSTNode
    {
      int data;//信息
      int bf;//平衡因子
      struct BSTNode *lchild,*rchild; //平衡树左右儿子指针
    }BSTNode,*BSTree;//平衡二叉排序树结构的定义
     核心算法:
        建立平衡排序二叉树算是算法中比较复杂的一个了,但是找到核心之后,也就只是比较复杂一些罢了,多练习一下即可。那核心是什么呢?要回答这个问题,就要深刻理解“平衡”和“排序”两个词的含义。顾名思义,平衡二叉树加上排序二叉树即是所要建的树。1.平衡二叉树要求每一个节点的左子树深度减去右子树的深度的绝对值要小于1。2.排序二叉树要求按照中序遍历该二叉树得到从小到大排序的序列。因此在每插入一个结点的时候都要判断是否平衡,1.若平衡则按照排序二叉树的方法插入之,然后刷新平衡因子即可。2.重要是不平衡的时候就要从最接近的不平衡的地方旋转的让它平衡,这样继续下去,不断插入就可以得到排序平衡二叉树。
        下面主要解释一下旋转方法:旋转共有4种方式,其实,最核心的只有两种基本操作即是L_Rotate( )和R_Rotate( ),分别是左旋和右旋。
    对于LL型,即是最先出错的节点平衡因子是2,左儿子平衡因子是1,运用一次右旋操作再刷新平衡因子即可。根据镜像对称原则,RR型与此是对应的,如法炮制即可。
    重要的并且复杂的是LR和RL操作,这两个操作也是镜像对称的只讲一个即可。就比如LR吧,最先出错的节点的平衡因子是2,该节点的左孩子的平衡因子是-1.则要对左孩子为根的子树进行左旋,然后对该最近节点进行右旋,刷新平衡因子即可。具体算法如下:

      1 /**************************两个基本操作*****************************/
      2 void L_Rotate(BSTree &p)  
      3 {
      4     //对以*p为根的二叉排序树做左旋处理,处理之后p指向新的树根结点
      5     //和R_Rotate()镜像对称
      6    BSTree rc;
      7    rc = p->rchild;
      8    p->rchild = rc->lchild;
      9    rc->lchild = p;
     10    p = rc;    
     11 }  
     12 void R_Rotate(BSTree &p)  
     13 {
     14     //对以*p为根的二叉排序树做右旋处理,处理之后p指向新的树根结点
     15     //注意此处引用的好处就是不用再出现双亲指针
     16    BSTree lc;
     17    lc=p->lchild; //指向B的位置
     18    p->lchild=lc->rchild; //此处转换仍保持中序遍历不变性
     19    lc->rchild=p; //更改a的指针位置
     20    p=lc;        //lc变成新的a
     21 }  
     22 /**********************四个旋转操作,每两个在一起*****************/
     23 //包含LL和LR
     24 void LeftBalance(BSTree &T)     
     25 {  //对已*T为根的二叉排序树做左平衡旋转处理
     26    BSTree lc,rd;
     27    lc = T->lchild; //lc调整左边
     28    switch(lc->bf)  
     29    {
     30      case LH://若是左边高则为LL型,只需旋转即可
     31          T->bf = lc->bf = EH; //旋转后平衡因子均为0
     32          R_Rotate(T); //LL型需要右旋转
     33          break;  
     34      case RH://若是右边高,则为LR型,需分两步调整
     35          rd = lc->rchild; //找到不确定因子rd
     36          switch(rd->bf)//对不确定因子进行讨论
     37          {
     38            case LH://左边高调整后
     39              T->bf = RH;//根节点右边变高
     40              lc->bf = EH; //lc变平衡
     41              break;
     42           case EH://rd有左右节点
     43             T->bf = lc->bf = EH; //调整后持平
     44             break;
     45           case RH://右边高
     46             T->bf = EH;//根节点平衡
     47             lc->bf = LH;//lc节点变成左边高
     48             break;  
     49          }  
     50         rd->bf=EH; //调整后rd节点一定平衡,且变成新的头节点
     51         L_Rotate(T->lchild); //1.先左旋
     52         R_Rotate(T);         //2.再右旋
     53    }
     54 }  
     55 /*************右平衡操作,包含RR和RL*******************************/
     56 void RightBalance(BSTree &T)  
     57 {
     58     //对已*T为根的二叉排序树做右平衡旋转处理
     59     //因为为LeftBalance(BSTree &T)的镜像,注释则省略
     60    BSTree lc,rd;
     61    lc = T->rchild;
     62    switch(lc->bf)
     63    {
     64     case RH://右边高,RR型
     65         T->bf = lc->bf = EH;
     66         L_Rotate(T);
     67         break;
     68     case LH://左边高,RL型,分两步旋转
     69        rd = lc->lchild;
     70        switch(rd->bf)
     71        {  
     72         case RH:
     73            T->bf = LH;
     74            lc->bf = EH;
     75            break;
     76         case LH:
     77            T->bf = EH;
     78            lc->bf = RH;
     79            break;
     80        case EH:
     81            T->bf = lc->bf = EH;
     82            break;
     83        }
     84        rd->bf = EH;
     85        R_Rotate(T->rchild); //1.先右旋
     86        L_Rotate(T);         //2.再左旋
     87    }
     88 }
     89     至于插入操作主要就是判断树是不是平衡,若不平衡是左边还是右边,对于添加的新节点改变了树的平衡了没有,改变了左边的还是右边的,然后进行相应的旋转处理。具体算法如下:
     90 int InsertAVL(BSTree &T, int key, bool &taller)  
     91 {
     92 //若在平衡二叉排序树中不存在与关键字key相等的结点,则将关键字插入树中
     93 //布尔变量taller表示树是否“长高”
     94   if(T==NULL)
     95   {
     96     T = (BSTree)malloc(sizeof(BSTNode));
     97     T->data = key;
     98     T->bf = EH;//叶子结点其平衡因子肯定为0
     99     T->lchild = T->rchild = NULL;
    100     taller = 1;//树长高了
    101   }
    102   else
    103   {
    104      if(key==T->data)
    105      {//如果树中已存放此关键字则不予插入
    106        taller = 0;
    107        return 0;
    108      }
    109      if(key<T->data)
    110      {//关键字小于根结点则插入其左子树中
    111           if(!InsertAVL(T->lchild,key,taller))
    112                return 0;
    113           if(taller)
    114           {//如果树长高了,判断是否平衡
    115             switch(T->bf)
    116             {
    117              case LH://若左边高,这次又加上一个左边的节点,则肯定变为2,即需要调整
    118                  LeftBalance(T);//不平衡时调用左平衡函数,使左子树平衡
    119                  taller = 0;
    120                  break;
    121              case EH://若相等,在左边加一个节点,则变为左边高
    122                 T->bf = LH;
    123                 taller = 1; //树变高
    124                 break;
    125              case RH://若右边高,在左边加一个节点,则持平
    126                 T->bf = EH;
    127                 taller = 0;
    128                break;
    129             }
    130           }
    131      }
    132      else
    133      {//插入右子树中
    134         if(!InsertAVL(T->rchild,key,taller))
    135           return 0;
    136         if(taller)
    137         {
    138           switch(T->bf)
    139           {
    140             case LH://同理,本来左边高,在右边加一个节点则持平
    141                 T->bf = EH;
    142                 taller = 0;
    143                 break;
    144             case EH://右边变高
    145                 T->bf = RH;
    146                 taller = 1;
    147                 break;
    148             case RH://右边不平衡了,需要调整
    149                 RightBalance(T);
    150                 taller = 0;
    151                 break;
    152           }
    153         }
    154      }
    155   }
    156         return 1;
    157 }  
    View Code

    4.4.理论与测试
      理论:如图,给定一个序列的节点数组,按照几种变换规则得到了图示的排序二叉树,可以得到三序遍历和平衡因子。(由于图形比较多,暂时为手工画图)
       该序列为:47, 63, 54,28,31,14,26,53,99,81
       先序遍历:31,26,14,28,54,47,53,81,63,99
       中序遍历:14,26,28,31,47,53,54,63,81,99
       平衡因子:31和47的平衡因子为-1,其他的都为0

    测试:运行程序之后输出为

    由先序和中序序列可以还原出树的原型,对照可知结果是正确的。
    4.5.讨论与体会
       排序二叉树的中序遍历结果即为升序排列,但是运算速率不是最高的,为了寻找更好的方法,平衡排序二叉树便诞生了。对于开创者而言这是令人称赞的,但是对于后学者来说,在学习算法的核心思想的同时,更重要的是别人是怎样想到的,当一个现实生活的需要放在眼前是,我们要有开创的眼光,具有创新能力,这点是非常重要的,因为对于应用上来说有了第一个其他的就不再令人惊奇了。同时,递归,引用,开关、选择分支语句的运用也要引起注意。学习图的最好方法,就是数形结合,一定要多画图。
    4.6.附录(源代码)

      1 #include<stdio.h> 
      2 #include<stdlib.h>
      3 #include<iostream>
      4 using namespace std;
      5 #define LH  1//左边高
      6 #define EH  0//一样高
      7 #define RH  -1//右边高
      8 typedef struct BSTNode
      9 {
     10   int data;//信息
     11   int bf;//平衡因子
     12   struct BSTNode *lchild,*rchild; //平衡树左右儿子指针
     13 }BSTNode,*BSTree;//平衡二叉排序树结构的定义
     14  
     15 void R_Rotate(BSTree &p)  
     16 {
     17     //对以*p为根的二叉排序树做右旋处理,处理之后p指向新的树根结点
     18     //注意此处引用的好处就是不用再出现双亲指针
     19    BSTree lc;
     20    lc=p->lchild; //指向B的位置
     21    p->lchild=lc->rchild; //此处转换仍保持中序遍历不变性
     22    lc->rchild=p; //更改a的指针位置
     23    p=lc;        //lc变成新的a
     24 }  
     25 void L_Rotate(BSTree &p)  
     26 {
     27     //对以*p为根的二叉排序树做左旋处理,处理之后p指向新的树根结点
     28     //和R_Rotate()镜像对称
     29    BSTree rc;    
     30    rc = p->rchild;
     31    p->rchild = rc->lchild;
     32    rc->lchild = p;
     33    p = rc;    
     34 }  
     35 //包含LL和LR
     36 void LeftBalance(BSTree &T)     
     37 {  //对已*T为根的二叉排序树做左平衡旋转处理
     38    BSTree lc,rd;
     39    lc = T->lchild; //lc调整左边
     40    switch(lc->bf)  
     41    {
     42      case LH://若是左边高则为LL型,只需旋转即可
     43          T->bf = lc->bf = EH; //旋转后平衡因子均为0
     44          R_Rotate(T); //LL型需要右旋转
     45          break;  
     46      case RH://若是右边高,则为LR型,需分两步调整
     47          rd = lc->rchild; //找到不确定因子rd
     48          switch(rd->bf)//对不确定因子进行讨论
     49          {
     50            case LH://左边高调整后
     51              T->bf = RH;//根节点右边变高
     52              lc->bf = EH; //lc变平衡
     53              break;
     54           case EH://rd有左右节点
     55             T->bf = lc->bf = EH; //调整后持平
     56             break;
     57           case RH://右边高
     58             T->bf = EH;//根节点平衡
     59             lc->bf = LH;//lc节点变成左边高
     60             break;  
     61          }  
     62         rd->bf=EH; //调整后rd节点一定平衡,且变成新的头节点
     63         L_Rotate(T->lchild); //1.先左旋
     64         R_Rotate(T);         //2.再右旋
     65    }
     66 }  
     67 /*************右平衡操作,包含RR和RL*******************************/
     68 void RightBalance(BSTree &T)  
     69 {
     70     //对已*T为根的二叉排序树做右平衡旋转处理
     71     //因为为LeftBalance(BSTree &T)的镜像,注释则省略
     72    BSTree lc,rd;
     73    lc = T->rchild;
     74    switch(lc->bf)
     75    {
     76     case RH://右边高,RR型
     77         T->bf = lc->bf = EH;
     78         L_Rotate(T);
     79         break;
     80     case LH://左边高,RL型,分两步旋转
     81        rd = lc->lchild;
     82        switch(rd->bf)
     83        {  
     84         case RH:
     85            T->bf = LH;
     86            lc->bf = EH;
     87            break;
     88         case LH:
     89            T->bf = EH;
     90            lc->bf = RH;
     91            break;
     92        case EH:
     93            T->bf = lc->bf = EH;
     94            break;
     95        }
     96        rd->bf = EH;
     97        R_Rotate(T->rchild); //1.先右旋
     98        L_Rotate(T);         //2.再左旋
     99    }
    100 }
    101  
    102 int InsertAVL(BSTree &T, int key, bool &taller)  
    103 {
    104 //若在平衡二叉排序树中不存在与关键字key相等的结点,则将关键字插入树中
    105 //布尔变量taller表示树是否“长高”
    106   if(T==NULL)
    107   {
    108     T = (BSTree)malloc(sizeof(BSTNode));
    109     T->data = key;
    110     T->bf = EH;//叶子结点其平衡因子肯定为0
    111     T->lchild = T->rchild = NULL;
    112     taller = 1;//树长高了
    113   }
    114   else
    115   {
    116      if(key==T->data)
    117      {//如果树中已存放此关键字则不予插入
    118        taller = 0;
    119        return 0;
    120      }
    121      if(key<T->data)
    122      {//关键字小于根结点则插入其左子树中
    123           if(!InsertAVL(T->lchild,key,taller))
    124                return 0;
    125           if(taller)
    126           {//如果树长高了,判断是否平衡
    127             switch(T->bf)
    128             {
    129              case LH://若左边高,这次又加上一个左边的节点,则肯定变为2,即需要调整
    130                  LeftBalance(T);//不平衡时调用左平衡函数,使左子树平衡
    131                  taller = 0;
    132                  break;
    133              case EH://若相等,在左边加一个节点,则变为左边高
    134                 T->bf = LH;
    135                 taller = 1; //树变高
    136                 break;
    137              case RH://若右边高,在左边加一个节点,则持平
    138                 T->bf = EH;
    139                 taller = 0;
    140                break;
    141             }
    142           }
    143      }
    144      else
    145      {//插入右子树中
    146         if(!InsertAVL(T->rchild,key,taller))
    147           return 0;
    148         if(taller)
    149         {
    150           switch(T->bf)
    151           {
    152             case LH://同理,本来左边高,在右边加一个节点则持平
    153                 T->bf = EH;
    154                 taller = 0;
    155                 break;
    156             case EH://右边变高
    157                 T->bf = RH;
    158                 taller = 1;
    159                 break;
    160             case RH://右边不平衡了,需要调整
    161                 RightBalance(T);
    162                 taller = 0;
    163                 break;
    164           }
    165         }
    166      }
    167   }
    168         return 1;
    169 }  
    170 //访问函数,输出节点以及相应平衡因子
    171 void VisitTree(BSTree &T)
    172 {  //输出结点
    173     if(T!=NULL)  
    174     {
    175       printf("%d ",T->data);  
    176       printf("平衡因子: %d
    ",T->bf);    
    177     }
    178 }  
    179 
    180 void PreOrderTraverse(BSTree &T)
    181 {//递归实现先序遍历
    182     if(T!=NULL)
    183     VisitTree(T);
    184     if(T->lchild)
    185       PreOrderTraverse(T->lchild);
    186     if(T->rchild)
    187      PreOrderTraverse(T->rchild);
    188 }  
    189 
    190 void InOrderTraverse(BSTree &T)
    191 {//递归实现中序遍历
    192     if(T->lchild)
    193       InOrderTraverse(T->lchild);
    194     if(T!=NULL)
    195        VisitTree(T);
    196     if(T->rchild)
    197       InOrderTraverse(T->rchild);
    198 }  
    199 int main(  )
    200 {
    201     BSTree T;
    202     bool taller=0;
    203     int i;
    204     T=NULL;
    205     int  a[50]={47,63,54,28,31,14,26,53,99,81};
    206     for(i=0;i<10;i++)
    207     {
    208         InsertAVL(T,a[i],taller);   
    209     }
    210      printf("先序遍历:
    ");   
    211      PreOrderTraverse(T);    
    212      printf("
    中序遍历:
    ");
    213      InOrderTraverse(T);    
    214      printf("
    ");
    215      return 0;
    216 }
    View Code
  • 相关阅读:
    30.过滤掉smb.conf配置文件中的空行和注释行和空白行(初级写法很不严谨)
    29. 分析文件内容,判断 fgets 共执行了多少次?
    28. 使用fgetc()/fputc()实现文件的加密与解密,存在溢出风险。
    27.读文件时通过两种方式判断文件结尾
    26. 使用fgetc()/fputc()实现文件的拷贝
    24. 练习定义几种指针及数组
    23. 实现 func()函数,在func()中,通过操作arr,实现修改str1到str2字符串,并打印所有字符串,考察对指针与数组与字符串的基本掌握
    Windows程序设计(Charles Petzold)HELLOWIN程序实现
    jvm 解释器和JIT编译器
    java 创建线程的方法
  • 原文地址:https://www.cnblogs.com/zyrblog/p/6876277.html
Copyright © 2011-2022 走看看