数据结构12————二叉树的遍历和建立

来源:互联网 发布:网络借贷管理办法 编辑:程序博客网 时间:2024/06/11 10:19

数据结构12————二叉树的遍历和建立

一.内容

1.二叉树遍历的概念

2.二叉树的遍历 使用递归实现

3.二叉树的遍历 使用栈实现

4.二叉树的建立

5.二叉树遍历的应用

所以代码只是核心函数,完整代码见末尾链接

注意本篇博客都是基于二叉链表的实现

二.二叉树遍历的概念

如果我们要求次序的不重复的,遍历一颗树,并且限制从左到右习惯方式,一共有4种不同遍历方法。
这里写图片描述

1.层次遍历

按照二叉树的层,一层一层的访问。

例:对于二叉树T而言,它的层次遍历序列为ABCDEFG

2.前序遍历

对于一颗二叉树

  • 先访问根节点
  • 前序遍历左子树(按照前序遍历规则访问左子树,即先访问左子树的根,然后继续遍历它的左右子树,下同)
  • 前序遍历右子树
    例:对于二叉树T而言,它的前序遍历序列为ABDGCEF

3.中序遍历

对于一颗二叉树
  • 中序遍历它的左子树
  • 访问它的根节点
  • 中序遍历它的右子树
    例:对于二叉树T而言,它的中序遍历序列为DGBAECF

4.后序遍历

对于一颗二叉树
  • 后序遍历它的左子树
  • 后序遍历它的右子树
  • 访问根节点
    例:对于二叉树T而言,它的后序遍历序列为GDBEFCA

三.二叉树的遍历 递归实现

其实根据上面的前中后序遍历规则,基本来说就可以确定递归的写法了,不过要增加一个递归出口。当前指针为NULL时,结束。

1.前序遍历

void PreOrder(BiTree root){    int static count;    if(root==NULL)        return;    printf("%c",root->data);    PreOrder(root->lChild);    PreOrder(root->rChild);}

2.中序遍历

void InOrder(BiTree root){    if(root==NULL)        return;    InOrder(root->lChild);    printf("%c",root->data);    InOrder(root->rChild);}

3.后序遍历

void PostOrder(BiTree root){    if(root==NULL)        return;    PostOrder(root->lChild);    PostOrder(root->rChild);    printf("%c",root->data);}

4.层次遍历

对于层次遍历而言,不能使用递归来进行遍历。要使用队来进行。(类似于图的广度优先遍历)

以树T为例
先将A入队 队内元素A 遍历序列为空
将队首A出队,访问,入队A的左右子树,队内元素BC,遍历序列A
将队首B出队,访问,入队B的左右子树,队内元素CD,遍历序列AB
将队首C出队,访问,入队C的左右子树,队内元素DEF,遍历序列ABC
将队首D出队,访问,入队D的左右子树, 队内元素EFG,遍历序列ABCD
将队首E出队,访问,入队E的左右子树, 队内元素FG,遍历序列ABCDE
将队首F出队,访问,入队F的左右子树, 队内元素G,遍历序列ABCDE
将队首G出队,访问,入队G的左右子树, 队内元素空,遍历序列ABCDEFG
队空,结束

void levelOrder(BiTree root){    BiTree p=root;    CSeQeue *S;    S=InitSeQueue();//创建队     InSeQueue(S,p);//入队     while(!EmptySeQueue(S)){        QutSeQueue(S,&p);//出队         printf("%c",p->data);        if(p->lChild!=NULL)            InSeQueue(S,p->lChild);//入队         if(p->rChild!=NULL)            InSeQueue(S,p->rChild);//入队     }}

三.二叉树的遍历 栈实现

1.前序遍历

对于前序遍历而言,使用栈来实现,和递归很像 规则,
  • 先访问根节点,然后将根节点入栈
  • 遍历左子树。遍历完之后,
  • 出栈,获得右子树指针,遍历它的右子树
    以树T为例:(可以自己根据代码走一遍)
    访问A节点,入栈,栈内元素A,遍历序列A
    访问A节点的左子树B,B入栈,栈内元素AB,遍历序列AB
    访问B节点的左子树D,D入栈,栈内元素ABD,遍历序列ABD
    访问D节点的左子树,左子树为空,左子树的访问完毕,出栈栈顶D,栈内元素AB,遍历序列为ABD
    访问D节点的右子树G,G入栈,栈内元素ABG,遍历序列ABDG
    访问G节点的左子树,左子树为空,出栈栈顶G,栈内元素AB,遍历序列ABDG
    访问G节点的右子树,右子树为空,出栈栈顶B,栈顶元素A,遍历序列为ABDG
    访问B节点的右子树,右子树为空,出栈栈顶A,栈内元素空,遍历序列ABDG
    访问A节点的右子树C,C入栈,栈内元素C,遍历序列ABDGC
    ……
    最后当需要出栈是,栈为空,则表示遍历完成
void PreOrder(BiTree root){    BiTree p;    SeqStack *S;    S = InitStack();    p = root;    while(p!=NULL||StackEmpty(S)==0){        while(p!=NULL){                 printf("%c",p->data);             Push(S,p); //入栈             p=p->lChild; //遍历左子树         }        if(StackEmpty(S)==0){            Pop(S,&p);//出栈             p=p->rChild; //遍历右子树         }     }   }

2.中序遍历

和前序遍历类似。
  • 先入栈,遍历它的左子树 左子树遍历完之后
  • 出栈,访问根节点,
  • 遍历右子树
void InOrder(BiTree root){    BiTree p;    SeqStack *S;    S = InitStack();    p = root;    while(p!=NULL||StackEmpty(S)==0){        while(p!=NULL){                 Push(S,p); //入栈             p=p->lChild; //遍历左子树         }        if(StackEmpty(S)==0){            Pop(S,&p);//出栈             printf("%c",p->data);             p=p->rChild; //遍历右子树         }     }   }

3.后序遍历

和前序中序遍历不同,后序遍历稍微麻烦些,因为涉及到两次访问栈顶

  • 先入栈,,遍历左子树,左子树遍历完之后
  • 访问栈顶(不出栈) 得到右子树指针,遍历右子树,右子树遍历完之后
  • 出栈栈顶元素,访问根节点
void PostOrder(BiTree root){    BiTree p,q;    SeqStack *S;    S = InitStack();    q = NULL;    p = root;    while(p!=NULL||StackEmpty(S)==0){        while(p!=NULL){ //出循环时p左子树为空             Push(S,p); //入栈              p=p->lChild; //遍历左子树         }        if(StackEmpty(S)==0){            GetTop(S,&p);            if(p->rChild==NULL||p->rChild==q){ //如果栈顶元素p右子树为空,或者上一步输出的是他的右子树                 Pop(S,&p);                printf("%c",p->data);                q=p;                p=NULL;             }else{ //如果上一步访问是栈顶元素的左子树                 p=p->rChild;            }        }    }} 

四.二叉树的建立

单独一个遍历序列是无法唯一确定一颗二叉树的,想要根据遍历序列确定一颗二叉树,只有两种方法,一种是根据扩展的遍历序列,一种是根据中序遍历序号和前序(中序)遍历序列。所以我们二叉树的创建就是根据这两种思路来进行的。 我们以扩展的先序遍历序列和先序+中序遍历序列为例创建二叉树

1.根据扩展的先序序列创建二叉树.

a. 扩展的先序序列

扩展的先序序列,就是将二叉树节点的左右空子树也用特殊符号表示出来。
比如树T的扩展序列为ABD^G^^^CE^^F^^

b. 代码
BiTree CreatBiTree(BiTree root){  //二叉树的建立(由扩展的先序序列建立的二叉树)     static int count;    char ch=str[count];    count++;    if(ch=='#')        return NULL;    root = (BiTNode *)malloc(sizeof(BiTNode));    root->data = ch;    root->lChild = CreatBiTree(root->lChild);//以当前节点的左指针为下一级二叉树的跟     root->rChild = CreatBiTree(root->rChild);//以当前节点的右指针为下一级二叉树的跟       return root;}

2.根据先序和中序序列创建二叉树

思路,根据先序序列确定根,根据中序序列确定根节点的左右子树。然后根据先序序列确定左右子树的根节点,根据中序序列确定左右子树的左右子树….

BiTree CreatBiTree(char *frontArray,char *centreArray,int n){    BiTree root;    char lfArray[N],rfArray[N];    char lcArray[N],rcArray[N];    int ln,rn,i,j;    char ch;    if(n==0)        return NULL;    ch = frontArray[0];    root = (BiTNode *)malloc(sizeof(BiTNode));    root->data = ch;    for(i=0;centreArray[i]!=ch;i++){        //左子树的后序         lcArray[i] = centreArray[i];    }    ln=i;    i++;    for(rn=0;i<n;rn++,i++){               //右子树的后序                                rcArray[rn] = centreArray[i];    }    for(i=0;i<ln;i++){                   //左子树的先序         lfArray[i] = frontArray[i+1];    }    for(j=0;j<rn;j++,i++){              //右子树的先序         rfArray[j]=frontArray[i+1];    }      root->lChild = CreatBiTree(lfArray,lcArray,ln);//以当前节点的左指针为下一级二叉树的跟     root->rChild = CreatBiTree(rfArray,rcArray,rn);//以当前节点的右指针为下一级二叉树的跟     return root;}

五.二叉树的遍历的应用

1. 节点及所在层次

这里写图片描述

void PreOrder(BiTree root){    //先序遍历输出加层次     int static count;    if(root==NULL)        return;    count++;        printf("(%c,%d)",root->data,count);    PreOrder(root->lChild);    PreOrder(root->rChild);    count--;}

2. 某层叶子节点个数

这里写图片描述

int f1(BiTree root,int k){       int static countlevel;    int static countleaf;    if(root==NULL)        return;    countlevel++;       if(countlevel == k&&root->lChild==NULL&&root->rChild==NULL)        countleaf++;    f1(root->lChild,k);    f1(root->rChild,k);    countlevel--;    return countleaf;}

3. 交换左右子树

这里写图片描述

void exchange(BiTree root){     //交换各节点的左右子树     BiTree t;    if(root==NULL)        return;    t=root->lChild;    root->lChild=root->rChild;    root->rChild=t;    exchange(root->lChild);    exchange(root->rChild);}

4. 根节点到叶子节点的路径

这里写图片描述

void PreOrder(BiTree root){         int static count;    int i;    if(root==NULL)        return;    count++;        if(root->lChild==NULL&&root->rChild==NULL){        printf("%c:",root->data);        for(i=1;i<count;i++){            printf("%c",array[i]);        }        printf("\n");    }else{        array[count]=root->data;    }    PreOrder(root->lChild);    PreOrder(root->rChild);    count--;}

5. 两个节点的共同祖先

这里写图片描述

int  count; int  flag; //标记是否找到 void PreOrder(BiTree root,char ch,char *array){      if(root==NULL)        return ;    count++;        if(root->data==ch){        flag = 1;        array[count]=0;        return ;    }else if(flag ==0&&root!=NULL){        array[count]=root->data;        }    if(flag==0){        PreOrder(root->lChild,ch,array);        PreOrder(root->rChild,ch,array);    }    count--;    return;}

在text12中

原创粉丝点击