二叉树的递归和非递归遍历

二叉树的递归和非递归遍历。递归遍历很简单,非递归遍历乍看起来很难,理解之后也是挺简单的。
1、二叉树的节点结构:

struct Node 
{
    DataType data;
    Node *left;
    Node *right;
};

2、用递归实现二叉树的先序、中序、后序遍历:

首先看下二叉树的递归序,代码如下:

void f(Node *node)
{
    if (nullptr == node)
    {
   		 return;
    }
    //1
    f(node->left);
    //2
    f(node->right);
    //3
}

如果在//1、//2、//3处都做一次打印本节点的操作,则流程如下:

图片

这样打印出来的序列就是递归序。

在递归序的基础上可以加工出三种形式的遍历:先序,中序,后序。

先序(头左右):即在递归序的基础上,第一次遇到节点的时候打印,后两次遇到不打印,比如上面递归序的先序遍历过程为:

1打印,2打印,4打印,4不打印,4不打印,2不打印,5打印,5不打印,5不打印,2不打印,1不打印,3打印,6打印,6不打印,6不打印,3不打印,7打印,7不打印,7不打印,3不打印,1不打印。

这样先序遍历之后的结果就是1、2、4、5、3、6、7。

中序(左头右):即在递归序的基础上,第二次遇到节点的时候打印。中序遍历之后的结果为:
4、2、5、1、6、3、7。

后序(左右头):即在递归序的基础上,第三次遇到节点的时候打印。后序遍历之后的结果为:

4、5、2、6、7、3、1。

代码如下:

//先序遍历递归算法

#include <iostream>
void preOrder(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    std::cout << node->data << " ";
    preOrder(node->left);
    preOrder(node->right);
}

//中序遍历递归算法

#include <iostream>
void inOrder(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    inOrder(node->left);
    std::cout << node->data << " ";
    inOrder(node->right);
}

//后序遍历递归算法

#include <iostream>
void postOrder(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    postOrder(node->left);
    postOrder(node->right);
    std::cout << node->data << " ";
}

3、用非递归实现二叉树的先序、中序、后序遍历:

任何递归都可以改成非递归。

(1)先序遍历的非递归:

分析:递归算法是系统压栈,所以非递归序要我们自己准备一个栈,实现手动压栈。栈的特点是后 进先出。而先序遍历是先头节点后左再右。那我们就利用的栈的特点,首先试一下将头节点1压栈,然后出栈打印1。接下来我们想打印2,假如让2先进栈,3后进栈,这时出栈的话就会打印3,不符合我们的需要。所以我们需要1的右孩子3先进栈,左孩子2后进栈,然后出栈打印2。再让2的右孩子5进栈,左孩子4进栈,出栈打印4。4无左右孩子,然后再出栈打印5,5无左右孩子,然后出栈打印3,让3的右孩子7进栈,再3的左孩子6进栈。出栈打印6,6无左右孩子,出栈打印7,7无左右孩子,栈空结束。

图片

算法步骤:

1、根节点压栈。

2、出栈一个节点cur。

3、打印(处理)cur。

4、先将节点cur右孩子(若有)压栈,再将节点cur左孩子(若有)压栈。

5、重复2-4步骤,直至栈空结束。

代码实现如下:

//先序遍历非递归算法

#include <iostream>
#include <stack>
void preOrderNoRecur(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    std::stack<Node*> nodeStack;
    nodeStack.push(node);
    while (!nodeStack.empty())
    {
        Node *cur = nodeStack.top();
        nodeStack.pop();
        std::cout << cur->data << " ";
        if (nullptr != cur->right)
        {
            nodeStack.push(cur->right);
        }
        if (nullptr != cur->left)
        {
            nodeStack.push(cur->left);
        }
    }
}

(2)后序遍历的非递归:

分析:先序遍历顺序是头左右,我们已知其非递归算法压栈时是头右左。假如我们想要一种遍历顺序是头右左,那么其非递归算法压栈时只需改成头左右就可以。而头右左的遍历顺序是后序遍历顺序左右头的相反顺序,我们自然就想到如果将头右左的遍历顺序压栈,再将栈依次出栈打印,则得到左右头的顺序,这就是后序遍历。

图片

算法步骤:

1、根节点进栈A

2、栈A出栈节点cur进辅助栈B

3、节点cur的左孩子(若有)进栈A,节点cur的右孩子(若有)进栈A

4、重复2-3步骤,直至栈Α空

5、依次出栈Β打印结果

代码实现如下:

//后序遍历非递归算法

#include <iostream>
#include <stack>
void postOrderNoRecur(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    std::stack<Node*> nodeStackA;
    std::stack<Node*> nodeStackB;//辅助栈
    nodeStackA.push(node);//根节点进栈
    while (!nodeStackA.empty())
    {
        Node *cur = nodeStackA.top();
        nodeStackA.pop();
        nodeStackB.push(cur);//cur节点进辅助栈
        if (nullptr != cur->left)
        {
            nodeStackA.push(cur->left);
        }
        if (nullptr != cur->right)
        {
            nodeStackA.push(cur->right);
        }
    }
    //辅助栈B依次出栈打印
    while (!nodeStackB.empty())
    {
        Node *cur = nodeStackB.top();
        nodeStackB.pop();
        std::cout << cur->data << " ";
    }
}

(2)中序遍历的非递归:

分析:中序遍历顺序是左头右。试想一下,任何一棵二叉树皆可被左子树分解,如果将每一个左子树依次入栈,即头入栈、左子树入栈,那么出栈顺序就是左->头。出栈时将当前节点的右孩子的左子树也依次入栈,则最后出栈时就是左->头->(右)左->头 …… 这样连起来就是左头右的顺序。

图片

算法步骤:

1、每棵子树,整棵树左边界依次进栈,直至没有左边界

2、依次出栈打印

3、对于出栈的每个节点右孩子,重复1-2步骤,直至栈空结束。

代码实现如下:

//中序遍历非递归算法

#include <iostream>
#include <stack>
void inOrderNoRecur(Node *node)
{
    if (nullptr == node)
    {
        return;
    }
    Node *p = node;//临时指针变量指向根节点,以防指针移动时改变根节点指针位置。
    std::stack<Node*> nodeStack;
    while (!nodeStack.empty() || nullptr != p)
    {
        if (nullptr != p)//左边界依次入栈
        {
            nodeStack.push(p);
            p = p->left;
        }
        else //左边界到底时出栈
        {
            p = nodeStack.top();
            nodeStack.pop();
            std::cout << p->data << " ";//出栈打印
            p = p->right;//p指向当前节点的右孩子
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值