二叉树的后序遍历

Posted 、工藤新一

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树的后序遍历相关的知识,希望对你有一定的参考价值。


二叉树的三种遍历方式,其中每一种遍历方式都有三种实现方式。

节点定义:

struct TreeNode
{
    int val;
    TreeNode *left,*right;
    TreeNode(int val){
        this->val = val;
        this ->left = this->right = NULL;
    }
};

后序遍历

后序遍历:先访问左孩子,然后访问右孩子,最后访问根节点。

所以,上面遍历的结果是:DAEHSCG。

下面,我们来看看具体代码实现:

1.递归实现

void PostOrder(TreeNode *root){
    if (root==NULL)
        return;
    PostOrder(root->left);
    PostOrder(root->right);
    cout<<root->val<<endl;
}

2.使用辅助栈

void postOrder(TreeNode *root) { 
    if(root == NULL)
        return;

    stack<TreeNode *> stk;
    stk.push(root);
    TreeNode *prev = NULL;
    while(!stk.empty()) {
        TreeNode *pNode = stk.top();
        if(!prev || prev->left == pNode || prev->right == pNode) {  // traverse down
            if(pNode->left)
                stk.push(pNode->left);
            else if(pNode->right)
                stk.push(pNode->right);
         /* else {
                cout << pNode->val << endl;
                stk.pop();
            }
        */
        }
        else if(pNode->left == prev) {  // traverse up from left
            if(pNode->right)
                stk.push(pNode->right);
        }
    /* else if(pNode->right == prev) { // traverse up from right
                cout << pNode->val << endl;
                stk.pop();
        }
    */
        else {
            cout << pNode->val << endl;
            stk.pop();
        }
        prev = pNode;
    }
}

双辅助栈实现思路:

设置两个栈stk, stk2;
    将根结点压入第一个栈stk;
    弹出stk栈顶的结点,并把该结点压入第二个栈stk2;
    将当前结点的左孩子和右孩子先后分别入栈stk;
    当所有元素都压入stk2后,依次弹出stk2的栈顶结点,并访问之。
    第一个栈的入栈顺序是:根结点,左孩子和右孩子;于是,压入第二个栈的顺序是:根结点,右孩子和左孩子。
    因此,弹出的顺序就是:左孩子,右孩子和根结点。

void PostOrder2(TreeNode *root){ //两个栈实现
    if (root == NULL)
        return;

    stack<TreeNode*> stk,stk2;
    stk.push(root);
    while(!stk.empty()){
        TreeNode* pNode = stk.top();
        stk.pop();
        stk2.push(pNode);// 将根节点压栈
        if (pNode->left != NULL) // 如果左孩子不为空,则压栈
        {
            stk.push(pNode->left);
        }
        if (pNode->right != NULL) // 如果左孩子不为空,则压栈
        {
            stk.push(pNode->right);
        }
    }
    while(!stk2.empty()){
        cout<<stk2.top()->val<<endl;
        stk2.pop();
    }
}

3.Morris遍历实现

实现思路:

1.先建立一个临时结点dummy,并令其左孩子为根结点root,将当前结点设置为dummy;

2.如果当前结点的左孩子为空,则将其右孩子作为当前结点;

3.如果当前结点的左孩子不为空,则找到其在中序遍历中的前驱结点,

-如果前驱结点的右孩子为空,将它的右孩子设置为当前结点,将当前结点更新为当前结点的左孩子;

-如果前驱结点的右孩子为当前结点,倒序输出从当前结点的左孩子到该前驱结点这条路径上所有的结点。将前驱结点的右孩子设置为空,将当前结点更新为当前结点的右孩子。

4.重复以上过程,直到当前结点为空。

具体实现:

void reverse(TreeNode* p1,TreeNode *p2){
    if (p1 == p2)
        return;
    TreeNode* x = p1;
    TreeNode* y = p1->right;

    while(true){
        TreeNode* tmp = y->right;
        y->right = x;
        x = y;
        y = tmp;
        if (x == p2)
            break;
    }
}
void printReverse(TreeNode* p1,TreeNode *p2){
    reverse(p1,p2);
    TreeNode* pNode = p2;
    while(true){
        cout<<pNode->val<<endl;
        if (pNode == p1)
            break;
        pNode = pNode->right;
    }
    reverse(p2,p1);
}
void PostOrder3(TreeNode* root){
    if(root == NULL)
        return;

    TreeNode *dummy = new TreeNode(-1);
    dummy->left = root;
    TreeNode *pNode = dummy;
    while(pNode != NULL) {
        if(pNode->left == NULL)
            pNode = pNode->right;
        else {
            TreeNode *pPrev = pNode->left;
            while(pPrev->right != NULL && pPrev->right != pNode)
                pPrev = pPrev->right;

            if(pPrev->right == NULL) {
                pPrev->right = pNode;
                pNode = pNode->left;
            }
            else {
                printReverse(pNode->left, pPrev);
                pPrev->right = NULL;
                pNode = pNode->right;
            }
        }
    }
}

附:二叉树的先序遍历

二叉树的先序遍历

附:二叉树的中序遍历

二叉树的中序遍历

附:二叉树的后序遍历

二叉树的后序遍历

附:二叉树的三种遍历对比及用图片展现说明

二叉树的三种遍历对比及用图片展现说明

以上是关于二叉树的后序遍历的主要内容,如果未能解决你的问题,请参考以下文章

怎么根据二叉树的前序,中序,确定它的后序

145. 二叉树的后序遍历

二叉树的后序遍历

二叉树的后序遍历(简单)

二叉树的后序遍历

LeetCode二叉搜索树的后序遍历序列