题解 | #二叉树的后序遍历#

二叉树的中序遍历

http://www.nowcoder.com/practice/1b25a41f25f241228abd7eb9b768ab9b

总结二叉树的非递归遍历思路
非递归一般是用栈实现,那么就要明确栈是 先入后出 的原则。
1. 前序遍历


前序遍历要求的输出顺序是根、左、右,那么结合栈的特点,入栈顺序就应该是右、左、根的顺序。
程序编写的时候需要一个栈和一个辅助指针,辅助指针tmp记录当前节点,首先把辅助指针指向的当前节点入栈(相当于根节点入栈),这样栈不为空的情况下,进行以下循环:弹出当前节点值(),放到返回数组res中,然后先在右子树不为空的时候入栈右子树在左子树不为空的时候入栈左子树,这样就可以保证栈顶元素弹出顺序为根、左、右。
图片说明

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector
     */
    vector<int> preorderTraversal(TreeNode* root) {
        // write code here
        vector<int> res;
        if(root==NULL)
        {
            return res;
        }
        stack<TreeNode*> TreeStack;
        TreeNode* tmp = root;
        TreeStack.push(tmp);
        while(TreeStack.size() > 0)
        {
            TreeNode* node = TreeStack.top();
            TreeStack.pop();
            res.push_back(node->val);
            if(node->right != NULL)
            {
                TreeStack.push(node->right);
            }
            if(node->left != NULL)
            {
                TreeStack.push(node->left);
            }
        }
        return res;
    }
};

2. 中序遍历


中序遍历要求的输出顺序是左、根、右,那么结合栈的特点,入栈顺序就应该是右、根、左的顺序。
程序编写的时候需要一个栈和2个辅助指针,一个辅助指针tmp记录当前节点,先将指针访问的数据入栈,一直访问其左孩子,直到将左孩子全部入栈;此时栈不为空,定义另一个辅助指针node指针,该指针指向栈顶元素,也就是最后一个左孩子。之后栈内有内容就取栈顶元素,放到返回数组中,同时每取一个栈顶元素,就把tmp指向其右孩子,这样就可以把根节点的左子树的所有左孩子,同时保证在有右孩子的情况下会将右孩子也入栈。然后又会在右孩子也有孩子的情况下再次遍历入栈,出栈的时候也会保证先左再中再右。
图片说明

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

//#include <vector>
//#include <stack>
//using namespace std;

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector
     */
    vector<int> inorderTraversal(TreeNode* root) {
        // write code here
        vector<int> res;//记录出栈数值
        stack<TreeNode*> Treestack;//定义栈Treestack,栈中类型为TreeNode*
        TreeNode* tmp = root;
        if(tmp == NULL)
        {
            return res;
        }

        while(Treestack.size() > 0 || tmp!=NULL)
        {
            while(tmp!=NULL)
            {
                Treestack.push(tmp);//左侧节点入栈
                tmp=tmp->left;
            }
            if(Treestack.size() > 0)
            {
                TreeNode* node = Treestack.top();//取栈顶元素
                Treestack.pop();//出栈
                res.push_back(node->val);
                tmp = node->right;
            }
        }
        return res;
    }
};

3. 后序遍历


后序遍历要求的输出顺序是左、右、根,那么结合栈的特点,入栈顺序就应该是根、右、左的顺序。
后序遍历是最复杂的,需要一个栈和2个辅助指针,一个辅助指针current记录当前节点,另一个辅助指针pre记录上一个访问的节点(或者叫上一个被弹出的节点)。
将根节点压入栈中,再遍历访问左子树的左孩子,并且压入栈中;之后弹栈顶元素,当前栈顶元素为整棵树最左边的节点,对于后序遍历,他一定是第一位,所以放到返回数组res里面,满足的前提条件首先有它是个叶子节点,也就是右孩子为空(为什么不强调左孩子,因为在进行弹出元素之前已经确定了它没有左孩子)。接下来就是用pre记录当前节点,将current置空,方面接受下一步。由于栈内仍有元素,那么大循环不结束,但此时current为空,不会压入当前节点的左孩子,而是继续取栈顶元素赋给current,此时current记录的就是前面pre的父节点,那么就要判断current有没有未被访问到的右孩子,也就是条件(current->right != NULL && current->right != pre),有的话就要将其压入栈,也就是current=current->right,这样可以保证栈中的顺序从下到上是父、右、左。

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector
     */
    vector<int> postorderTraversal(TreeNode* root) {
        // write code here
        vector<int> res;
        if(root == NULL)
        {
            return res;
        }
        stack<TreeNode*> TreeStack;
        TreeNode* current = root;//记录当前节点
        TreeNode* pre = NULL;//记录前一个节点
        while(TreeStack.size()>0 || current != NULL)
        {
            if(current != NULL)
            {
                TreeStack.push(current);
                current=current->left;
            }
            else
            {
                current=TreeStack.top();
                if(current->right != NULL && current->right != pre)
                {
                    current = current->right;
                }
                else
                {
                    TreeStack.pop();
                    res.push_back(current->val);
                    pre = current;
                    current = NULL;
                }
            }
        }
        return res;
    }
};

第二种方式
注意以下代码在牛客上直接提交会报内存超额,但是这样写便于理解,为了解决内存超额,可以把一些循环判断的条件进行改写。

22行改为 if(!current)
32行改为if(!node->left && !node->right)
40行改为
while(TreeStack.size()>0 && (pre->left || pre->right) && (pre->left==node || pre->right==node))
50行和52行分别改为
if(node->right)
if(node->left)

/**
 * struct TreeNode {
 *    int val;
 *    struct TreeNode *left;
 *    struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector
     */
    vector<int> postorderTraversal(TreeNode* root) {
        // write code here
        TreeNode* current = root;
        TreeNode* pre = NULL;
        stack<TreeNode*> TreeStack;
        vector<int> res;
        if(current == NULL)
        {
            return res;
        }
        //根节点是最后被访问到,所以先入栈根节点
        TreeStack.push(current);
        //保证访问顺序是左右根,那么就要先入栈右节点
        while(TreeStack.size()>0)
        {
            TreeNode* node = TreeStack.top();//取栈顶元素判断
            if(node->left==NULL && node->right==NULL)
            {//说明node是叶子节点,要么是左节点,要么是右节点,
             //那么还需要判断node的前一个结点是不是栈顶结点的父节点
             //如果不是,只弹出node;如果是就要把父节点和node一起弹出。
             //不管是哪种情况,都要先弹node
                res.push_back(node->val);
                TreeStack.pop();
                pre = TreeStack.top();//取当前栈顶节点,判断pre是否是node的父节点
                while(TreeStack.size()>0 && (pre->left==NULL || pre->right==NULL) && (pre->left==node || pre->right==node))
                {//判断条件是要满足:栈不为空、当前栈顶有左右孩子,并且某一个孩子就是node
                    node = pre;
                    res.push_back(pre->val);
                    TreeStack.pop();
                    pre = TreeStack.top();
                }
            }
            else
            {//说明node有孩子,那么就要先入右孩子,再入左孩子
                if(node->right!=NULL)
                    TreeStack.push(node->right);
                if(node->left!=NULL)
                    TreeStack.push(node->left);
            }
        }
        return res;
    }
};

图解:
图片说明

牛客刷题记录 文章被收录于专栏

记录自己的刷题记录,刷过的题的解法

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务