美文网首页数据结构和算法分析工作生活
二叉树遍历(递归&非递归实现)

二叉树遍历(递归&非递归实现)

作者: 就良同学 | 来源:发表于2019-06-29 21:40 被阅读12次
先序遍历 中序遍历 后序遍历
根结点-左子树-右子树 左子树-根子树-右子树 左子树-右子树-根结点

递归实现:

//先序遍历
void preOrder(Btree T){
    if(T){
        putchar(T->data);
        preOrder(T->lchild);
        preOrder(T->rchild);
    }
}

//中序遍历
void inOrder(Btree T){
    if(T){
        inOrder(T->lchild);
        putchar(T->data);
        inOrder(T->rchild);
    }
}

//后序遍历
void postOrder(Btree T){
    if(T){
        postOrder(T->lchild);
        postOrder(T->rchild);
        putchar(T->data);
    }
}

非递归实现:

基于栈的递归消除:

递归(recursion)就是子程序(或函数)直接调用自己或通过一系列调用语句间接调用自己。该执行过程要求每一次节点中断调用其他方法,必须记录下该中断节点的环境信息,作用是为了调用结束返回结果之后原程序能够从上次中断位置起继续执行,在计算机中,通过一个结构来实现该效果(栈:先进后出):

image

但递归算法的执行效率较低,这是因为递归需要反复入栈,时间和空间的开销都比较大。

(从本质上来看,递归其实是方便了程序员却难为了机器)

为了避免这种开销,需要消除递归,方法主要有两种:

1.针对简单递归,利用迭代代替递归;

2.利用栈的方式实现:利用人工栈模拟系统堆栈。(这种方法通用性强,但是本质上还是递归的,区别是将计算机做的事情改由人工来完成)

利用栈实现的非递归过程可分为 如下步骤

  1. 设置一个工作栈,用于保存递归工作记录,包括实参、返回地址等;
  2. 将调用函数传递而来的参数和返回地址入栈;
  3. 利用循环模拟递归分解过程,逐层将递归过程的参数和返回地址入栈。当满足递归结束条件时,依次逐层退栈,并将结果返回给上一层,直至栈空为止。

非递归遍历二叉树:

先序遍历:根结点-左子树-右子树

算法思想:

从二叉树的根结点开始,将根结点设为当前结点p,执行以下操作:

  1. 边遍历边打印,并存入栈中(一开始当前结点p为根结点)→先打印当前结点p的数据→再将当前结点p入栈→若左孩子存在,将左孩子设为当前结点→重复执行此操作,直至到达二叉树左子树最左下角;
  2. 栈顶元素出栈,将栈顶元素设为当前结点p→若当前结点p的右孩子存在,将右孩子设为当前结点p(进入右子树)→回到步骤1,重复执行 1、2,直至栈空。
            void preOrder(Btree T){
                    Bnode *p=T;
                    Bnode *stack[MaxSize];
                    int top=0;
                    if(T==NULL){
                            return;
                    }
                    while(p!=NULL||top>0){
                    
                            //边遍历边打印,并存入栈中,以后需要借助这些根节点进入右子树
                            while(p!=NULL){
                                    putchar(p->data);
                                    stack[top++]=p;
                                    p=p->lchild;
                            }
                            //当p为空时,说明根和左子树都遍历完了,该进入右子树了
                            if(top>0){
                                    p=stack[--top];
                                    p=p->rchild;
                            }
                    
                    /*
                            //另一种写法
                            if(p!=NULL){
                                    putchar(p->data);
                                    stack[top++]=p;
                                    p=p->lchild;
                            }else{
                                    p=stack[--top];
                                    p=p->rchild;
                            }
                    */
                    }
            }

中序遍历:左子树-根子树-右子树

算法思想

从二叉树的根结点开始,将根结点设为当前结点p,执行以下操作:

  1. 当前结点p入栈(一开始当前结点p为根结点)→若当前结点p存在左孩子→将左孩子设为当前结点p→重复此操作,直至到达二叉树左子树最左下角;
  2. 将栈顶元素出栈,将出栈元素设为当前结点p→打印该结点p的数据;
  3. 若当前结点p存在右孩子,则将右孩子设为当前 结点p→回到步骤1,重复执行 1、2、3,直至栈空。
            void inOrder(Btree T){
                    Bnode *p=T;
                    Bnode *stack[MaxSize];
                    int top=0;
                    if(T==NULL){
                            return;
                    }
                    while(p!=NULL||top>0){
                    
                            //重复将当前结点的左孩子入栈
                            while(p!=NULL){
                                    stack[top++]=p;
                                    p=p->lchild;
                            }
                            //出栈
                            if(top>0){
                                    p=stack[--top];
                                    putchar(p->data);
                                    p=p->rchild;
                            }
                    
                    /*
                    if(p!=NULL){
                            stack[top++]=p;
                            p=p->lchild;
                    }else{
                            p=stack[--top];
                            putchar(p->data);
                            p=p->rchild;
                            }
                    */
                    }   
            }

后序遍历:左子树-右子树-根结点

算法思想

声明两个结点指针(用于临时存储):当前结点-pCur;上一次访问的结点-pLast,将根结点设为当前结点pCur:

  1. 当前结点入栈(一开始当前结点为根结点)→若当前结点存在左孩子→将左孩子设为当前结点→重复此操作,直至到达二叉树左子树最左下角;

  2. 取栈顶元素,并设为当前结点pCur:

  • 若当前结点pCur没有右孩子or右孩子刚刚访问过了(上一次访问的结点pLast就是右孩子)→栈顶元素出栈,打印当前结点pCur的数据→将当前结点pCur赋给pLast→将当前结点置空;
  • 若当前结点pCur有右孩子→将右孩子设为当前结点(遍历右子树)→回到步骤1,重复执行 1、2,直至栈空。
            void postOrder(Btree T){
                    Bnode *pCur=T,*pLast;//pCur为当前结点,pLast为上一次访问的结点
                    Bnode *stack[MaxSize];//定义栈用来存放结点的指针
                    int top=0;//栈顶指针
                    if(T==NULL){
                            return;
                    }
                    while(pCur!=NULL||top>0){
                            
                            //重复将当前结点的左孩子入栈,直到把pCur移动到左子树最下边
                            while(pCur!=NULL){
                                    stack[top++]=pCur;
                                    pCur=pCur->lchild;
                            }
                            
                            if(top>0){
                                    pCur=stack[top-1];//取栈顶元素
                                    if(pCur->rchild==NULL||pCur->rchild==pLast){//若当前结点没有右孩子or右孩子刚刚访问过了
                                            putchar(pCur->data);//打印当前结点
                                            pLast=pCur;//将当前结点赋给pLast
                                            pCur=NULL;//将当前结点置空
                                            top--;//栈顶元素出栈
                                    }else{
                                            pCur=pCur->rchild;
                                    }
                            }
                                    
                            /*
                            if(pCur!=NULL){
                                    stack[top++]=pCur;
                                    pCur=pCur->lchild;
                            }else{
                                    pCur=stack[top-1];//取栈顶元素
                                    if(pCur->rchild==NULL||pCur->rchild==pLast){//若当前结点没有右孩子or右孩子刚刚访问过了
                                            putchar(pCur->data);//打印当前结点
                                            pLast=pCur;//将当前结点赋给pLast
                                            pCur=NULL;
                                            top--;
                                    }else{
                                            pCur=pCur->rchild;
                                    }
                            }
                            */
                    }
            }

相关文章

  • 算法之二叉树

    二叉树之C++实现 创建二叉树 复制二叉树 先序遍历 递归实现 非递归实现 中序遍历 递归实现 非递归实现 后序遍...

  • 总结

    1、二叉树广度遍历(非递归) 广度遍历非递归实现需要依靠一个队列。 2、二叉树深度遍历(递归与非递归,前序,中序和...

  • 二叉树,非递归法

    递归法 二叉树的递归,有前序遍历、中序遍历、后序遍历,一般采用递归法,比较简单 非递归法 二叉树非递归法,采用栈来实现

  • 数据结构-树的遍历

    1. 先序遍历 递归实现 非递归实现 2. 中序遍历 递归实现 非递归实现 3. 后序遍历 递归实现 非递归实现 ...

  • 左神算法笔记——Morris遍历

    基本问题——实现二叉树的前序、中序、后序遍历 (递归、非递归,mirros方法) 递归 递归方式下的前中后遍历 非...

  • 二叉树遍历java,非递归、层次。

    /** * 前序遍历 * 递归 */ /*** 前序遍历* 非递归*/ 后续遍历非递归 二叉树层次遍历基于java...

  • 二叉树的遍历实现递归与非递归

    本文实现了二叉树的深度遍历算法,分为递归与非递归 递归的实现非常简单,基本上没啥难度 非递归的实现需要根据遍历的顺...

  • 二叉树前、中、后序遍历、层次遍历

    1、前序遍历 非递归:利用Stack实现 递归 2、中序遍历 非递归:利用Stack实现 递归: 3、后序遍历 非...

  • 二叉树的各类遍历方法

    二叉树的遍历主要有四种:前序、中序、后序、层序。 遍历的实现方式主要是:递归和非递归。递归遍历的实现非常容易,非递...

  • 二叉树递归非递归遍历算法整理

    一、二叉树前序遍历 1 前序递归遍历 2.前序非递归遍历 一、二叉树中序遍历 2.中序递归遍历 1.中序非递归遍历...

网友评论

    本文标题:二叉树遍历(递归&非递归实现)

    本文链接:https://www.haomeiwen.com/subject/vndmcctx.html