【树】二叉树的两种非递归遍历方法

本文介绍二叉树的非递归遍历方法,包括中序、后序和先序遍历,以及层次遍历。每种遍历方式都详细解释了算法流程,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非递归的遍历需要使用栈保存当前不输出的结点,并且三种遍历顺序步骤有所不同。

中序遍历

1.查看其当前结点是否为空:

若非空则将当前结点入栈,指针指向其左孩子;

若当前结点为空,说明上一个入栈的结点没有左孩子,将上一个结点出栈打印;

2.之后指针指向其右孩子

3.循环步骤1、2,直到栈为空(所有结点都遍历完出栈了)

void NiceInOrder(BTNode*p)
{
    if (p == NULL)return;
    stack<BTNode*>st;
    
    while (!st.empty()||p!=NULL)
    {
        while (p != NULL)
        {
            st.push(p);
            p = p->lchild;
        }
        p = st.top();st.pop();
        printf("%c ", p->data);
        p = p->rchild;
    }
    printf("\n");
}

后序遍历

后序遍历因为先输出左右子树再输出根(14行),这个过程中需要通过根结点,所以需要有一个值来记录该根结点的左右子树是否都已经遍历(5行)。

要注意打印之后指针要指向空(19行),若当前树是某棵树的左子树,下次循环时为空则不在遍历这棵树了,否则程序陷入死循环。

void NicePastOrder(BTNode* p)
{
    if (p == NULL)return;
    stack<BTNode*>st;
    BTNode* tag = NULL;//标记,指向遍历完右孩子的结点
    while (!st.empty() || p != NULL)
    {
        while (p != NULL)//遍历左子树
        {
            st.push(p);
            p = p->lchild;
        }
        p = st.top(); 
        if (p->rchild == NULL || p->rchild == tag)//右子树为空或遍历完,打印根
        {
            st.pop();
            printf("%c ", p->data);
            tag = p;//指向遍历完的根
            p = NULL;//表示左孩子已经遍历完了
        }
        else//否则指向右子树继续遍历
        {
            p = p->rchild;
        }
    }
    printf("\n");
}

先序遍历

先序遍历是根左右的顺序,先入根结点,出根;

之后入栈顶的右、左孩子(因为栈先进后出,所以要先入右孩子)

void NicePreOrder(BTNode* p)
{
    if (p == NULL)return;
    stack<BTNode*>st;
    st.push(p);
    while (!st.empty())
    {
        p = st.top();
        printf("%c ",p->data);
        st.pop();
        //先右后左入栈,先左后右出栈
        if (p->rchild != NULL)
        {
            st.push(p->rchild);
        }
        if (p->lchild != NULL)
        {
            st.push(p->lchild);
        }
    }
}

层次遍历

层次遍历需要使用队列

步骤:

1.入根结点及其左右孩子;

2.出队头元素,入队头结点的左右孩子(不为空)

3.若队列为空则遍历完该树

void LevelOrder(BTNode* p)
{
    if (p == NULL)return;
    queue<BTNode*>qu;
    qu.push(p);
    while (!qu.empty())
    {
        p = qu.front();
        printf("%c ",p->data);
        qu.pop();
        if(p->lchild!=NULL)
            qu.push(p->lchild);
        if (p->rchild != NULL)
            qu.push(p->rchild);
    }
    printf("\n");
}

另一种非递归遍历要将每个结点出栈的次数记下:定义一个结构体

struct StkNode
{
    BTNode* pnode;
    int popnum;//出栈次数
};

后序遍历:每个结点都需要三次出栈,访问完它的左右根和自己

结点第一次入栈时出栈次数为0

当栈不为空,栈顶元素出栈

若出栈时popnum==3(第三次出栈,说明左右子树都遍历了),输出该结点;

若出栈时popnum==1且其左孩子不为空,则再将其入栈,并入其左孩子。

若出栈时popnum==2且其右孩子不为空,则再将其入栈,并入其右孩子;

void NicePastOrder_2(BTNode* p)
{
    if (p = NULL)return;
    stack<StkNode>st;
    st.push(StkNode{ p,0 });
    while (!st.empty())
    {
        StkNode node = st.top(); st.pop();
        if (++node.popnum == 3)//左右子树都访问了
        {
            printf("%c ", node.pnode->data);
        }
        else
        {
            st.push(node);
            if (node.popnum == 1 &&p->lchild!=NULL)
            {
                st.push(StkNode{node.pnode->lchild,0});
            }
            else if (node.popnum == 2 && p->rchild != NULL)
            {
                st.push(StkNode{ node.pnode->rchild,0 });
            }
        }
    }
    printf("\n");
}

中序遍历将顺序改一下,前序遍历就不需要这样了,第一种方法比较方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曦樂~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值