二叉树的递归和非递归方式的三种遍历

二叉树的三种遍历方式,前序遍历中序遍历后序遍历,中的前中后都是指的是根节点的访问顺序,这三种遍历方式的概念在这里就不多说了,太普遍了!

二叉树的建立

我们这里以前序遍历为例:
我们先定义好结构体

struct Tree{
    Tree* lson;
    Tree* rson;
    int data;
};
Tree* T;

下面是前序建立二叉树:

void createBtiTree(Tree* &tree)
{
    int data;
    cin>>data;
    if(data==-1) {
        tree=NULL;
    }else {
        tree = new Tree();
        tree->data = data;
        createBtiTree(tree->lson);
        createBtiTree(tree->rson);
    }
}

二叉树的递归方式遍历

当我们觉得构造递归困难的时候(不知道在这里用构造这个词对不对),我们就可以把递归想象成为一棵树,这样就容易构造一些!

递归的三种遍历方式比较基础,在这里就不过多进行赘述,直接给出代码!如果读者有困难的话,可以试着在纸上描述一下,毕竟递归是要深入的东西!

前序遍历

void preOrderRecur(Tree* tree)
{
    if(tree==NULL) return ;
    cout<<tree->data<<" ";
    preOrderRecur(tree->lson);
    preOrderRecur(tree->rson);
}

中序遍历

void inOrderRecur(Tree* tree)
{
    if(tree==NULL) return ;
    inOrderRecur(tree->lson);
    cout<<tree->data<<" ";
    inOrderRecur(tree->rson);
}

后序遍历

void posOrderRecur(Tree* tree)
{
    if(tree==NULL) return ;
    posOrderRecur(tree->lson);
    posOrderRecur(tree->rson);
    cout<<tree->data<<" ";
}

非递归形式的二叉树的三种遍历方式

用递归解决的问题都能用非递归的方式解决。因为递归是使用函数栈来保存信息的,如果自己用自己申请的数据结构来代替函数栈就能实现相同的功能!

前序遍历

前序遍历也是最简单的一种,分为下面三个步骤:

  1. 申请一个栈,将头结点压入栈
  2. 弹出栈顶指针,记作:cur,如果这个这个指针有右孩子,将右孩子入栈,如果有左孩子,将左孩子入栈;
  3. 不断重复过程2,直到栈为空,结束程序!
实现代码
void PreOrderRecur(Tree* tree)
{
    while(!sta.empty()) sta.pop();
    sta.push(tree);
    while(!sta.empty()) {
        Tree* cur  = sta.top();
        sta.pop();
        cout<<cur->data<<" ";
        if(cur->rson!=NULL) sta.push(cur->rson);
        if(cur->lson!=NULL) sta.push(cur->lson);
    }
}

中序遍历

  1. 申请一个栈,头结点为开始节点(当前节点)
  2. 如果当前节点不为空,那么将左节点压栈,即做tree=tree->lson操作,如果当前节点为空的时候打印栈顶元素,并且出栈,将 当前节点变为栈顶元素的右节点也就是做tree = cur->rson(中序遍历中,栈主要保存的是父节点元素)
  3. 不断重复步骤2直到栈空,结束程序!
实现代码
void InOrderRecur(Tree* tree)
{
    while(!sta.empty()) sta.pop();
    while(!sta.empty() || tree!=NULL) {
        if(tree==NULL)  {
            Tree* cur = sta.top();
            sta.pop();
            cout<<cur->data<<" ";
            tree=cur->rson;
        } else {
            sta.push(tree);
            tree=tree->lson;
        }
    }
}

后序遍历

后序遍历用栈实现起来相对前面两种遍历是难实现一些,这这里给出两个方法:第一种方法是用两个栈来实现的(比较好理解),第二种是用一个栈来实现的!

两个栈的方法实现

先说两个栈来实现的方法,第一个栈保存的是根节点元素,第二栈是保存输出的元素!过程如下:

  1. 申请一个栈,将根节点入栈
  2. 如果栈不为空,弹出第一个栈的栈顶元素记做cur,将第一个栈顶元素出栈,然后将cur压入第二个栈。如果cur有左孩子将左孩子加入第一个栈,如果有右孩子将右孩子加入第一个栈
  3. 不断的重复步骤2,直到第一个栈为空,打印第二个栈,结束程序!

实现代码

void PosOrderRecur(Tree* tree)
{
    while(!sta.empty()) sta.pop();
    while(!Sta.empty()) Sta.pop();
    sta.push(tree);
    while(!sta.empty()) {
        Tree* cur = sta.top();
        sta.pop();Sta.push(cur);
        if(cur->lson!=NULL) sta.push(cur->lson);
        if(cur->rson!=NULL) sta.push(cur->rson);
    }
    while(!Sta.empty()) {
        cout<<Sta.top()->data<<" ";
        Sta.pop();
    }
}
一个栈的实现

我们用cur表示栈顶元素,h表示的是最近栈的元素,初始化时h为头结点。算法流程如下:

  1. 申请一个栈 ,将头结点压栈,初始化h变量,
  2. 如果栈不为空,cur赋为栈顶元素!
    ---- 1.如果cur的左孩子不为NULL并且h不等于cur的左孩子也不等于cur的右孩子那么就将左孩子入栈。(如果最近h等于当前节点的左孩子,就说明左子树已经打印完了,否则就代表还没有打印过,就应该将左孩子或者右孩子入栈)
    ---- 2.在1条件不成立的条件下,并且cur的右孩子不等于h并且不为空,就说明右子树还没有处理过,这个时候就应该将cur的右孩子入栈!
    ---- 3.如果前俩个条件都不成立,就说明cur的左子树和右子树已经打印完毕了,或者当前节点为叶子节点,此时就应该将栈顶元素出栈了,并且令h=cur
  3. 一直重复步骤2直到栈为空,结束程序
实现代码
void TPosOrderRecur(Tree* tree)
{
    while(!sta.empty()) sta.pop();
    sta.push(tree);
    Tree* c=tree;
    while(!sta.empty()) {
        Tree* cur = sta.top();
        if(cur->lson!=NULL && cur->lson!=c && cur->rson!=c){
            sta.push(cur->lson);
        } else if(cur->rson!=NULL && c!=cur->rson) {
            sta.push(cur->rson);
        } else{
            sta.pop();
            c=cur;
            cout<<cur->data<<" ";
        }
    }
}

如果对我的文章感兴趣可以关注的公众号:云影原生。
在这里插入图片描述

  • 12
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值