• 二叉树的遍历(递归和非递归)


    二叉树的遍历

    1、前序遍历

    (1)递归的前序遍历

    前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。

    //前序遍历,递归实现
    void D_PreOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
            cout << pRoot->data << " ";
            D_PreOrder(pRoot->left);
            D_PreOrder(pRoot->right);
    
        }
    }

    (2)非递归的前序遍历

    根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。

    因此其处理过程如下:

    对于任一结点P:
    1) 访问结点P,并将结点P入栈;
    2) 判断结点P的左孩子是否为空

    若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);
    
    若不为空,则将P的左孩子置为当前的结点P;
    

    3) 直到P为NULL并且栈为空,则遍历结束。

    //前序遍历,非递归实现,用栈实现
    void ND_PreOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            while (pRoot || !bntStack.empty())
            {
                if (pRoot)
                {
                    cout << pRoot->data << " ";
                    bntStack.push(pRoot);
                    pRoot = pRoot->left;
    
                }
                else
                {
                    pRoot = bntStack.top();
                    bntStack.pop();   //已经输出之后的值直接弹出删除
                    pRoot = pRoot->right;
                }
            }
        }
    }

    2、中序遍历

    (1)递归的中序遍历

    中序遍历按照“左孩子-根结点-右孩子”的顺序进行访问。

    //中序遍历,递归实现
    void D_MidOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
    
            D_MidOrder(pRoot->left);
            cout << pRoot->data << " ";
            D_MidOrder(pRoot->right);
    
        }
    }
    

    (2)非递归的中序遍历

    根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:

    对于任一结点P,
    1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
    2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
    3)直到P为NULL并且栈为空则遍历结束

    //中序遍历,非递归实现,用栈实现
    void ND_MidOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            while (pRoot || !bntStack.empty())
            {
                if (pRoot)
                {
    
                    bntStack.push(pRoot);
                    pRoot = pRoot->left;
    
                }
                else
                {
                    pRoot = bntStack.top();
                    cout << pRoot->data << " ";
                    bntStack.pop();//已经输出之后的值直接弹出删除
                    pRoot = pRoot->right;
                }
            }
        }
    }

    3、后序遍历

    (1)递归的后序遍历

    后序遍历按照“左孩子-右孩子-根结点”的顺序进行访问。

    //后序遍历,递归实现
    void D_PostOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
    
            D_PostOrder(pRoot->left);
            D_PostOrder(pRoot->right);
            cout << pRoot->data << " ";
        }
    }
    

    (2)非递归的后序遍历

    后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。

    情况1:当前节点的左孩子以及右孩子同时为空的时候,才会将当前节点值输出

    情况2:当前一个访问的是当前节点的左孩子或者右孩子的时候,那么此时也会将当前节点的值输出

    具体的思路就是上述所表述的,具体的步骤如下:

    (1)初始化,将根节点入栈,将前一个访问的置为NULL;

    (2)如果栈不为空,取栈顶元素作为当前节点的值,判断当前节点是否满足两种输出情况,如果满足输出,出栈,将当前节点的值赋给pre,即前一个访问节点,进行下次循环;如果不满足进行步骤3

    (3)如果当前节点的左孩子以及右孩子都不为空,现将右孩子入栈,再入栈左孩子;如果一个为空,那么入栈不为空的

    (4)循环(2)到(3),直至栈为空的时候截止。

    //后序遍历,非递归实现,用栈实现
    void ND_PostOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            bntStack.push(pRoot);
            BinaryTreeNode *PreNode = NULL;//前一次访问的节点 
    
            while (!bntStack.empty())
            {
                pRoot = bntStack.top();
    
                //叶子节点或左右子节点都已经访问过的节点,
    
                if ((!pRoot->left&&!pRoot->right) 
                    || PreNode && 
                    ((PreNode == pRoot->left) || (PreNode == pRoot->right)))
                    //之所以有PreNode &&是为了防止根节点只有一个分支  
                {
                    cout << pRoot->data << " ";
                    PreNode = pRoot;
                    bntStack.pop();
    
                }
                else
                {
                    if (pRoot->right) //空节点不入栈  
                        bntStack.push(pRoot->right);//将后访问的节点先入栈 
                    if (pRoot->left)
                        bntStack.push(pRoot->left);
    
                }
            }
        }
    }
    
    

    4、二叉树遍历的完整代码:

    #include <iostream>
    #include <stack>
    
    using namespace std;
    
    //二叉树结点数据结构
    struct BinaryTreeNode
    {
        int data;
        BinaryTreeNode *left;
        BinaryTreeNode *right;
    };
    
    //创建二叉树结点
    BinaryTreeNode *CreatBinaryTreeNode(int val)
    {
        BinaryTreeNode *pNode = new BinaryTreeNode;
        pNode->data = val;
        pNode->left = NULL;
        pNode->right = NULL;
    
        return pNode;
    }
    
    //链接二叉树节点
    void ConnectBinaryTreeNode(BinaryTreeNode *pParent, BinaryTreeNode *pLeft, BinaryTreeNode *pRight)
    {
        if (pParent != NULL)
        {
            pParent->left = pLeft;
            pParent->right = pRight;
        }
    }
    
    //销毁二叉树
    void DestoryBinaryTree(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
            BinaryTreeNode *pLeft = pRoot->left;
            BinaryTreeNode *pRight = pRoot->right;
    
            delete pRoot;
            DestoryBinaryTree(pLeft);//递归删除
            DestoryBinaryTree(pRight);
    
        }
    }
    
    //前序遍历,递归实现
    void D_PreOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
            cout << pRoot->data << " ";
            D_PreOrder(pRoot->left);
            D_PreOrder(pRoot->right);
    
        }
    }
    
    //前序遍历,非递归实现,用栈实现
    void ND_PreOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            while (pRoot || !bntStack.empty())
            {
                if (pRoot)
                {
                    cout << pRoot->data << " ";
                    bntStack.push(pRoot);
                    pRoot = pRoot->left;
    
                }
                else
                {
                    pRoot = bntStack.top();
                    bntStack.pop();   //已经输出之后的值直接弹出删除
                    pRoot = pRoot->right;
                }
            }
        }
    }
    
    //中序遍历,递归实现
    void D_MidOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
    
            D_MidOrder(pRoot->left);
            cout << pRoot->data << " ";
            D_MidOrder(pRoot->right);
    
        }
    }
    
    //中序遍历,非递归实现,用栈实现
    void ND_MidOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            while (pRoot || !bntStack.empty())
            {
                if (pRoot)
                {
    
                    bntStack.push(pRoot);
                    pRoot = pRoot->left;
    
                }
                else
                {
                    pRoot = bntStack.top();
                    cout << pRoot->data << " ";
                    bntStack.pop();//已经输出之后的值直接弹出删除
                    pRoot = pRoot->right;
                }
            }
        }
    }
    
    //后序遍历,递归实现
    void D_PostOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot != NULL)
        {
    
            D_PostOrder(pRoot->left);
            D_PostOrder(pRoot->right);
            cout << pRoot->data << " ";
        }
    }
    
    //后序遍历,非递归实现,用栈实现
    void ND_PostOrder(BinaryTreeNode *pRoot)
    {
        if (pRoot)
        {
            stack<BinaryTreeNode *> bntStack;
            bntStack.push(pRoot);
            BinaryTreeNode *PreNode = NULL;//前一次访问的节点 
    
            while (!bntStack.empty())
            {
                pRoot = bntStack.top();
    
                //叶子节点或左右子节点都已经访问过的节点,
    
                if ((!pRoot->left&&!pRoot->right) 
                    || PreNode && 
                    ((PreNode == pRoot->left) || (PreNode == pRoot->right)))
                    //之所以有PreNode &&是为了防止根节点只有一个分支  
                {
                    cout << pRoot->data << " ";
                    PreNode = pRoot;
                    bntStack.pop();
    
                }
                else
                {
                    if (pRoot->right) //空节点不入栈  
                        bntStack.push(pRoot->right);//将后访问的节点先入栈 
                    if (pRoot->left)
                        bntStack.push(pRoot->left);
    
                }
            }
        }
    }
    
    
    int main()
    {
        BinaryTreeNode* pNode1 = CreatBinaryTreeNode(10);
        BinaryTreeNode* pNode2 = CreatBinaryTreeNode(6);
        BinaryTreeNode* pNode3 = CreatBinaryTreeNode(14);
        BinaryTreeNode* pNode4 = CreatBinaryTreeNode(4);
        BinaryTreeNode* pNode5 = CreatBinaryTreeNode(8);
        BinaryTreeNode* pNode6 = CreatBinaryTreeNode(12);
        BinaryTreeNode* pNode7 = CreatBinaryTreeNode(16);
    
        ConnectBinaryTreeNode(pNode1, pNode2, pNode3);
        ConnectBinaryTreeNode(pNode2, pNode4, pNode5); 
        ConnectBinaryTreeNode(pNode3, pNode6, pNode7);
    
        cout << "递归的前序遍历:";
        D_PreOrder(pNode1);
        cout << endl;
        cout << "非递归的前序遍历:";
        ND_PreOrder(pNode1);
        cout << endl;
    
        cout << "递归的中序遍历:";
        D_MidOrder(pNode1);
        cout << endl;
        cout << "非递归的中序遍历:";
        ND_MidOrder(pNode1);
        cout << endl;
    
        cout << "递归的后序遍历:";
        D_PostOrder(pNode1);
        cout << endl;
        cout << "非递归的后序遍历:";
        ND_PostOrder(pNode1);
        cout << endl;
    
        DestoryBinaryTree(pNode1);
    
        system("pause");
        return 0;
    }

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    2020年天梯赛补题报告
    补提报告...
    2020.11.14天梯赛练习*6 补题
    2020-11-08补题报告
    2020-10-30 — 补题报告
    10-24 补题
    2020.10.17天梯赛练习 和 16 号个人赛 补题报告
    elasticsearch DQL 有关表达式的设计
    tab键和空格键
    emacs配置python开发环境
  • 原文地址:https://www.cnblogs.com/yangquanhui/p/4937456.html
Copyright © 2020-2023  润新知