欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二叉树的非递归遍历

程序员文章站 2022-06-07 20:48:17
...

思路

无论是前中后序还是层序,均是需要借助栈来进行遍历,其中前中后序遍历均是模拟递归的过程。
递归遍历其实是函数在调用过程中调用自身,调用自身时将该层函数压栈,执行新的一层,以前序遍历为例

void BTreePrevOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->_data);
    BTreePrevOrder(root->_left);
    BTreePrevOrder(root->_right);
}

当函数执行BTreePrevOrder(root->_left);时,将开辟一片新的空间执行新的一层函数,上层函数将被压栈在新的这层函数执行结束后执行。
一层一层压栈、出栈实现了遍历的递归实现,而非递归就是模拟这个压栈、出栈的过程,下面就借助栈实现以下这个过程
二叉树的非递归遍历
接下来看一下访问节点的时机
二叉树的非递归遍历

完整的关于二叉树的代码下载

二叉树代码github地址
执行结果
二叉树的非递归遍历

前序遍历

前序遍历在入栈前访问,下面是实现代码

void BTreePrevOrderNonR(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    Stack* s = StackInit();
    BTNode* cur = root;
    while (cur || (StackEmpty(s) != 0))
    {
        while (cur)
        {
            printf("%d ", cur->_data);
            StackPush(s, cur);
            cur = cur->_left;
        }
        BTNode* front = StackTop(s);
        StackPop(s);
        cur = front->_right;
    }
    StackDestroy(s);
    printf("\n");
}

中序遍历

出栈时访问节点,不细讲

void BTreeInOrderNonR(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    Stack* s = StackInit();
    BTNode* cur = root;
    while (cur || (StackEmpty(s) != 0))
    {
        while (cur)
        {
            StackPush(s, cur);
            cur = cur->_left;
        }
        BTNode* front = StackTop(s);
        StackPop(s);
        printf("%d ", front->_data);
        cur = front->_right;
    }
    StackDestroy(s);
    printf("\n");
}

后序遍历

后序是最难控制的,访问条件和前序后序都不一样,后序访问顺序是左右根,出栈访问,同时需要记录访问的节点当做标记
和前序中序遍历不同,前中序遍历时,先出栈再判断右为不为空,不为空入栈,后序不同的地方是先判断右为不为空或者是否被访问过,如果没有,将右节点入栈,并不出栈父亲节点,否则才出栈父亲节点访问
因此只有在右节点为空或者右节点为上一个出栈的节点,则出栈目前的节点
同样是出栈访问节点,但是后序需要严格的条件才能出栈,总结一下:

1.右节点为空,可出栈
2.右节点不为空,但是为上一个访问的节点,可出栈

void BTreePostOrderNonR(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    Stack* s = StackInit();
    BTNode* cur = root;
    BTNode* last = NULL;//最后一个访问的节点
    while (cur || (StackEmpty(s) != 0))
    {
        while (cur)
        {
            StackPush(s, cur);
            cur = cur->_left;
        }
        BTNode* front = StackTop(s);
        if ((front->_right == NULL) || (front->_right == last))
        {
            printf("%d ", front->_data);
            StackPop(s);
            last = front;
        }
        else
        {
            cur = front->_right;
        }
    }
    printf("\n");
}

层序遍历

层序遍历借助的不是栈,栈的结构并不适合层序遍历,因为栈先进后出,而我们需要先进的先出,队列就很适合
思路是将头结点入队列,出栈的时候先访问,然后将出栈节点的左右节点入队列(如果左右节点不为空的话,为空不如队列),直至队列为空

void BTreePostOrderNonR(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    Stack* s = StackInit();
    BTNode* cur = root;
    BTNode* last = NULL;//最后一个访问的节点
    while (cur || (StackEmpty(s) != 0))
    {
        while (cur)
        {
            StackPush(s, cur);
            cur = cur->_left;
        }
        BTNode* front = StackTop(s);
        if ((front->_right == NULL) || (front->_right == last))
        {
            printf("%d ", front->_data);
            StackPop(s);
            last = front;
        }
        else
        {
            cur = front->_right;
        }
    }
    printf("\n");
}

本文使用的相关树结构体,队列,栈等结构代码

树结构体

typedef int BTreeDataType;

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* _left;
    struct BinaryTreeNode* _right;
    BTreeDataType _data;
}BTNode;

使用前需要将STDataType的类型改成BTNode*
栈的头文件github地址

队列

使用前需要将QueueDataType的类型改成BTNode*
队列的头文件github地址