zoukankan      html  css  js  c++  java
  • 二叉树前、中、后遍历详解【递归+迭代+morris】

    转自:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/leetcodesuan-fa-xiu-lian-dong-hua-yan-shi-xbian-2/

    1.递归解法

    前中后遍历都很简单,就不写了。

    2.迭代解法

    力扣144题,二叉树的前序遍历:前序遍历的迭代解法,也是比较简单,利用栈先入根节点,根节点弹出,再入右子节点和左子节点,那么之后就是左子节点先弹出进行遍历,右子节点们一直被压栈。

    class Solution {
    public:
        vector<int> preorderTraversal(TreeNode* root) {
            vector<int> ans;
            if(!root) return ans;
            stack<TreeNode*> st;
            st.push(root);
            while(!st.empty()){
                TreeNode* t=st.top();st.pop();
                ans.push_back(t->val);
                if(t->right)st.push(t->right);
                if(t->left)st.push(t->left);
            }
            return ans;
        }
    };

    力扣94题,二叉树的中序遍历:

    Java版本的代码:

    public static void inOrderIteration(TreeNode head) {
        if (head == null) {
            return;
        }
        TreeNode cur = head;
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || cur != null) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode node = stack.pop();
            System.out.print(node.value + " ");
            if (node.right != null) {
                cur = node.right;
            }
        }
    }
    
    作者:gre-z
    链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/leetcodesuan-fa-xiu-lian-dong-hua-yan-shi-xbian-2/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    内部的while循环表示处理左子树,然后node就是当前的节点,也就相当于此子树中的根节点,然后right了就是去遍历右子树了。

    3月份的时候学习过,当然过了7个月到现在已经全忘记了。。。

    力扣145题,二叉树的后序遍历:可以通过对前序遍历的栈来实现,后序遍历也用栈存储,前序中根先入遍历的时候弹出,左右入,弹出就是根右左的顺序了。

    class Solution {
    public:
        vector<int> postorderTraversal(TreeNode* root) {
            vector<int> ans;
            if(!root) return ans;
            stack<TreeNode*> pre,post;
            pre.push(root);
            while(!pre.empty()){
                TreeNode* top=pre.top();pre.pop();
                post.push(top);
                if(top->left)pre.push(top->left);
                if(top->right)pre.push(top->right);
            }
            while(!post.empty()){
                ans.push_back(post.top()->val);
                post.pop();
            }
            return ans;
        }
    };

    第二种解法:

    class Solution {
    public:
        vector<int> postorderTraversal(TreeNode* root) {
            vector<int> ans;
            if(!root) return ans;
            TreeNode* head=root;//指向的是访问的前一个节点
            stack<TreeNode*> st;
            st.push(root);
            while(!st.empty()){
                TreeNode* top=st.top();
                if(top->left!=NULL && top->left !=head && top->right!=head){
                    st.push(top->left);
                }else if(top->right!=NULL && top->right != head){//左为空或者左已被访问过。
                    st.push(top->right);
                }else{//如果左右子节点均为空,或者都已被访问过,那么就将当前ans
                    ans.push_back(top->val);
                    st.pop();//出栈的并且进入ans后才表示它被访问过,而且是下一个被访问的前一个节点,以此标注
                    head=top;
                }
            }
            return ans;
        }
    };

    https://www.cnblogs.com/grandyang/p/4251757.html,讲的很好。

    3.Morris解法

    https://www.cnblogs.com/anniekim/archive/2013/06/15/morristraversal.html,经典教程。

    二叉树的遍历和搜索时间复杂度是不一样的,前着一定是O(n),后者最优是O(logn),退化为链表的是O(n)。

    上面链接中给出了两段代码,我觉得关键还是要知道自己遍历一下,对两种不同的情况,print节点的时候意味着什么。这个搞清楚才可以。

    我终于知道这个意味着什么了!

    99题恢复二叉搜索树,也用到了这个解法:

    //中序遍历:
    void inorderMorrisTraversal(TreeNode *root) {
        TreeNode *cur = root, *prev = NULL;
        while (cur != NULL)
        {
            if (cur->left == NULL)          // 1.
            {//当左子树为空时,那么中序遍历就可以打印cur节点
                printf("%d ", cur->val);
                cur = cur->right;
            }
            else
            {
                // find predecessor
                prev = cur->left;
                while (prev->right != NULL && prev->right != cur)
                    prev = prev->right;
    
                if (prev->right == NULL)   // 2.a)
                {//这里表示去遍历左子树了。
                    prev->right = cur;
                    cur = cur->left;
                }
                else                       // 2.b)
                {//到这一步就意味着,左子树遍历完了,接下来要去遍历右子树了,所以可以打印当前节点(中序遍历)
                    prev->right = NULL;
                    printf("%d ", cur->val);
                    cur = cur->right;
                }
            }
        }
    }

    下面是先序遍历:

    void preorderMorrisTraversal(TreeNode *root) {
        TreeNode *cur = root, *prev = NULL;
        while (cur != NULL)
        {
            if (cur->left == NULL)
            {
                printf("%d ", cur->val);//如果左子树为空,那么可以打印当前
                cur = cur->right;
            }
            else
            {
                prev = cur->left;
                while (prev->right != NULL && prev->right != cur)
                    prev = prev->right;
    
                if (prev->right == NULL)
                {//这里将要去遍历左子树,因为是先根所以先打印一下cur节点
                    printf("%d ", cur->val);  // the only difference with inorder-traversal
                    prev->right = cur;
                    cur = cur->left;
                }
                else
                {//这里表示左子树和当前cur遍历完了,去遍历右子树
                    prev->right = NULL;
                    cur = cur->right;
                }
            }
        }
    }

    //至于morris后根,我还没看,代码好长啊

  • 相关阅读:
    Java中的IO基本用法
    Java中的字符串
    centos
    Linux 常用命令
    rand随机
    auto.js 学习
    毕业了!!!
    论文的查找
    kill的使用
    安装交叉编译工具arm-linux-gcc-4.3.2 并且修改环境变量
  • 原文地址:https://www.cnblogs.com/BlueBlueSea/p/13888630.html
Copyright © 2011-2022 走看看