二叉树

二叉树

一 二叉树的存储结构

1.1 顺序存储结构

#define MAX_SIZE  100
typedef telemtype sqbitree[MAX_SIZE];

1.2 链式存储结构

typedef struct BTNode
{
    ElemType  data ;
    struct BTNode  *Lchild , *Rchild;
}BTNode; 
typedef struct BTNode_3
{
    ElemType  data ;
    struct BTNode_3  *Lchild , *Rchild , *parent;
}BTNode_3; 

二 遍历二叉树及其应用

2.1 DLR——先(根)序遍历
2.1.1 递归算法

算法的递归定义是:

若二叉树为空,则遍历结束;否则
【1】 访问根结点;
【2】先序遍历左子树(递归调用本算法);
【3】先序遍历右子树(递归调用本算法)。

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

说明:visit()函数是访问结点的数据域,其要求视具体问题而定。树采用二叉链表的存储结构,用指针变量T来指向。

2.1.2 非递归算法

设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T;
【1】访问p所指向的结点;
【2】q=p->Rchild ,若q不为空,则q进栈;
【3】p=p->Lchild ,若p不为空,转(1),否则转(4);
【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);
    }
}
2.2 LDR——中(根)序遍历
2.2.1 递归算法

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

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

设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T
【1】若p不为空,p进栈, p=p->Lchild ;
【2】否则(即p为空),退栈到p,访问p所指向的结点;
【3】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);
    }
}
2.3 LRD——后(根)序遍历
2.3.1 递归算法

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

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

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

       其次,设两个堆栈S1、S2 ,S1保存结点,S2保存结点的状态标志变量tag 。S1和S2共用一个栈顶指针。
       设T是指向根结点的指针变量,非递归算法是:
      若二叉树为空,则返回;否则,令p=T;
【1】第一次经过根结点p,不访问:
     p进栈S1 , tag 赋值0,进栈S2,p=p->Lchild 。
【2】若p不为空,转(1),否则,取状态标志值tag :
【3】若tag=0:对栈S1,不访问,不出栈;修改S2栈顶元素值(tag赋值1) ,取S1栈顶元素的右子树,即p=S1[top]->Rchild ,转(1);
【4】若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);
    }
}
2.4 层次遍历二叉树

层次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。
       为保证是按层次遍历,必须设置一个队列,初始化时为空。
       设T是指向根结点的指针变量,层次遍历非递归算法是:
若二叉树为空,则返回;否则,令p=T,p入队;
【1】队首元素出队到p;
【2】访问p所指向的结点; 
【3】将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;    //左结点入队
        }
    }
}

三 二叉树遍历算法的应用

3.1 二叉树的二叉链表创建

3.1.1 按满二叉树方式建立

        在此补充按满二叉树的方式对结点进行编号建立链式二叉树。对每个结点,输入i:结点编号,按从小到大的顺序输入、ch:结点内容,假设是字符。

        在建立过程中借助一个一维数组S[n] ,编号为i的结点保存在S[i]中。

#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;
}
3.1.2 按先序遍历方式建立

二叉树的扩充方法是:

在二叉树中结点的每一个空链域处增加一个扩充的结点(总是叶子结点,用方框“”表示)

对于二叉树的结点值:

  • 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); 
    }
}

3.2 求二叉树的叶子结点数

可以直接利用先序遍历二叉树算法求二叉树的叶子结点数。只要将先序遍历二叉树算法中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);
}

3.3 求二叉树的深度

利用层次遍历算法可以直接求得二叉树的深度。

#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;
            }
        }
    }
}

四 线索树

Ltag:

  • 0Lchild域指示结点的左孩子

  • 1Lchild域指示结点的前驱

Rtag:

  • 0Rchild域指示结点的右孩子
  • 1Rchild域指示结点的后继
typedef struct BiThrNode{
    ElemType data;
    struct BiTreeNode *Lchild, *Rchild; 
    int Ltag, Rtag;
}BiThrNode;

说明:画线索二叉树,虚线表示线索,指向前驱或后继;实线表示指针,指向左、右孩子。

4.1 线索化二叉树

仿照线性表的存储结构,在二叉树的线索链表上也添加一个头结点head,头结点的指针域的安排是:

  • Lchild域:指向二叉树的根结点;
  • Rchild域:指向中序遍历时的最后一个结点;
  • 二叉树中序序列中的第一个结点Lchild指针域和最后一个结点Rchild指针域均指向头结点head。
#define  MAX_NODE   50

/*  Link=0表示指针, Thread=1表示线索   */
typedef enmu{
    Link,
    Thread
}PointerTag;

typedef struct BiThrNode{
    ElemType  data;
    struct BiTreeNode *Lchild, *Rchild; 
    PointerTag  Ltag, Rtag;
}BiThrNode;

4.1.1 先序线索化二叉树
void preorder_Threading(BiThrNode *T)
 {
    BiThrNode  *stack[MAX_NODE];
    BiThrNode  *last=NULL, *p ;
    int top=0 ;
    if  (T!=NULL)
    {
        stack[++top]=T;
        while (top>0)
        {
            p=stack[top--] ;
            if(p->Lchild!=NULL)  p->Ltag=0 ;
            else
            {
                p->Ltag=1;
                p->Lchild!=last;
            }
                if(last != NULL)
                if(last->Rchild != NULL)
                    last->Rtag = 0;
                else  
                {
                    last->Rtag = 1;
                    last->Rchild = p;
                }
                last = p;
                if(p->Rchild!=NULL) 
                    stack[++top]=p->Rchild; 
                if (p->Lchild!=NULL)
                    stack[++top]=p->Lchild;
        }
        Last->Rtag = 1;//最后一个结点是叶子结点
    }
}
4.1.2 中序线索化二叉树
void inorder_Threading(BiThrNode *T)
{
    BiThrNode *stack[MAX_NODE];
    BiThrNode *last = NULL, *p=T;
    int top=0 ;
    while(p!=NULL||top>0)
        if(p!=NULL)
        {
            stack[++top]=p;
            p=p->Lchild;
        }
        else
        {
            p=stack[top--] ;
            if(p->Lchild!=NULL)
                p->Ltag=0 ;
            else
            {
                p->Ltag=1;
                p->Lchild=last;
            }
            if  (last!=NULL)
                if (last->Rchild!=NULL)
                    last->Rtag=0 ;
                else
                {
                    last->Rtag=1;
                    last->Rchild=p;
                }
            last=p ; 
            P = p->Rchild; 
        }
    last->Rtag=1;  //最后一个结点是叶子结点
}

4.2 线索二叉树的遍历

4.2.1 先序线索二叉树的先序遍历
void preorder_Thread_bt(BiThrNode *T)
{
    BiThrNode  *p=T ;
    while (p!=NULL)
    {
        visit(p->data);
        if (p->Ltag == 0)
            p=p->Lchild;
        else
            p=p->Rchild;
    }
}
4.2.2 中序线索二叉树的中序遍历
void inorder_Thread_bt(BiThrNode *T)
{
    BiThrNode *p;
    if(T!=NULL)
    {
        p = T;
        while(p->Ltag == 0)
            p=p->Lchild;         //寻找最左的结点
        while  (p!=NULL) 
        {
            visit(p->data) ;
            if(p->Rtag == 1)    
                p=p->Rchild;     //通过右线索找到后继
            else                  //否则,右子树的最左结点为后继
            {
                p=p->Rchild; 
                while(p->Ltag==0)
                    p=p->Lchild; 
            }
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值