二叉树的前序、中序、后序的遍历(递归&非递归)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hansionz/article/details/81910587

前言:二叉树的非递归遍历需要用到栈的一些操作,所以贴出栈的相关操作的链接
https://blog.csdn.net/hansionz/article/details/81636557


定义数据结构
typedef char BTDataType;    
typedef struct BinaryTreeNode
{
    BTDataType _data;
    struct BinaryTreeNode* _left;
    struct BinaryTreeNode* _right;
}BTNode;

1.前序遍历

递归:按照前序遍历的规则,先访问根节点,在递归遍历左子树,最后递归遍历右字树,整个问题可以转换成好多个子问题进行求解

void BinaryTreePrevOrder_R(BTNode* root)
{
    //树为空,直接返回
    if (root == NULL)
    {
        return;
    }
    //访问根
    printf("%c", root->_data);
    //递归访问左子树
    BinaryTreePrevOrder_R(root->_left);
    //递归访问右子树
    BinaryTreePrevOrder_R(root->_right);
}

非递归:利用栈的后进先出性质
这里写图片描述

void BinaryTreePrevOrder(BTNode* root)
{
    Stack s;
    StackInit(&s);
    BTNode* cur = root;
    //cur不等于空说明左路还有结点没有访问,栈不为空说明还有右树未曾访问
    while (cur || StackEmpty(&s) != 0)
    {
        //1.访问左路结点,并压栈
        while (cur)
        {
            printf("%c", cur->_data);
            StackPush(&s, cur);
            cur = cur->_left;
        }
        //2.从栈里出来,说明左树已经被访问过了
        BTNode* top = StackTop(&s);
        StackPop(&s);
        //3.子问题访问右树
        cur = top->_right;
    }
}

2.中序遍历

递归:按照中序遍历的规则,先递归遍历左子树,在访问根节点,最后递归遍历右字树,整个问题可以转换成好多个子问题进行求解

void BinaryTreeMidOrder_R(BTNode* root)
{
    //树为空,直接返回
    if (root == NULL)
    {
        return;
    }
    //递归访问左子树
    BinaryTreeMidOrder_R(root->_left);
    //访问根
    printf("%c", root->_data);
    //递归访问右子树
    BinaryTreeMidOrder_R(root->_right);
}

非递归:利用栈的性质,先将左路结点全部压栈,出栈就说明栈顶元素左子树已经遍历完了,这时把栈顶元素的右子树当做是一个子问题来看待,遍历它的右子树,直到cur和栈都为空,则所有结点都被遍历完了
这里写图片描述

void BinaryTreeMidOrder(BTNode* root)
{
    Stack s;
    StackInit(&s);
    BTNode* cur = root;
    //cur不等于空说明左路还有结点没有压栈,栈不为空说明还有右树未曾访问
    while (cur || StackEmpty(&s) != 0)
    {
        //1.先将左路结点压栈
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }
        //2.出栈说明左树已经访问完了,现在访问根节点
        BTNode* top = StackTop(&s);
        printf("%c", top->_data);
        StackPop(&s);
        //子问题访问右树
        cur = top->_right;
    }
}

3.后序遍历

递归:按照后序遍历的规则,先递归遍历左子树,在递归遍历右字树,最后在访问根节点,整个问题可以转换成好多个子问题进行求解

void BinaryTreePostOrder_R(BTNode* root)
{
    //树为空,直接返回
    if (root == NULL)
    {
        return;
    }
    //递归访问左子树
    BinaryTreePostOrder_R(root->_left);
    //递归访问右子树
    BinaryTreePostOrder_R(root->_right);
    //访问根
    printf("%c", root->_data);
}

非递归:后序遍历的非递归相比前序、和中序的非递归比较难一些,因为后序遍历是先遍历左子树,在遍历右子树,最后访问根节点。当我们将左路的所有结点压栈,当要出栈遍历它的右子树的时候,我们并不知道右子树是否已经遍历过了,这点必须想个办法解决。
这里写图片描述

void BinaryTreePostOrder(BTNode* root)
{
    Stack s;
    StackInit(&s);
    BTNode* cur = root;
    BTNode* prev = NULL;
    while (cur || StackEmpty(&s))
    {
        //1.将左路结点压栈
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }
        //2.取栈顶,出栈说明左路结点已经访问
        BTNode* top = StackTop(&s);
        //3.如果当前结点右树为空,则直接访问该节点;
        //  如果右树存在,并且已经访问过,则直接访问它
        //(prev是前一个被访问过的结点,如果右树的根节点等于prev,则说明右树被访问过)
        if ((top->_right == NULL)||(prev == top->_right))
        {
            printf("%c", top->_data);
            prev = top;
            StackPop(&s);
        }
        //如果右树存在,并且没有访问过,则去访问它的右树(子问题)
        else
        {
            cur = top->_right;
        }
    }
}

总结:二叉树的三中遍历,递归方法比较容易理解,但是非递归的方法是比较难理解的,建议以此篇博客学习的同学根据图和代码自己走上一遍,就可以更好的理解。

猜你喜欢

转载自blog.csdn.net/hansionz/article/details/81910587