二叉树的三种遍历方式(递归+非递归)

总思路

首先树的遍历一共有三种

  • 前序遍历
    顺序是 根结点 - 左子树 - 右子树
  • 后序遍历
    顺序是 左子树 - 右子树 - 根结点
  • 中序遍历
    顺序是 左子树 - 根结点 - 右子树

并且对于每个结点都按照一样的顺序递归执行

递归

递归方式下,大家都很熟悉,无非就是函数体内根据遍历的顺序来调用。

void preorder(TreeNode* root) {
    
    
        if(root == nullptr) {
    
    
            return;
        }
        ans.push_back(root ->val);
        preorder(root -> left);
        preorder(root -> right);
    }

按照这样的格式修改顺序即可,如果想要知道这样为什么可以实现,可以在懂得分析递归过程的前提下自己走一遍就知道了,就是扩展,回溯的过程。如果还不会分析递归过程,那不要硬记,去多学学递归再做。
递归实现:

前序遍历

Leetcode - 144

class Solution {
    
    
public:
    vector<int> ans;
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        if(root == nullptr) return {
    
    };
        preorder(root);
        return ans;
    }
    void preorder(TreeNode* root) {
    
    
        if(root == nullptr) {
    
    
            return;
        }
        ans.push_back(root ->val);
        preorder(root -> left);
        preorder(root -> right);
    }
};

中序遍历

Leetcode - 94

class Solution {
    
    
public:
    vector<int> ans;
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        if(root == nullptr) return {
    
    };
        inorder(root);
        return ans;
    }
    void inorder(TreeNode* root) {
    
    
        if(root == nullptr) {
    
    
            return;
        }
        inorder(root -> left);
        ans.push_back(root ->val);
        inorder(root -> right);
    }
};

后序遍历

Leetcode - 145

class Solution {
    
    
public:
    vector<int> ans;
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        if(root == nullptr) return {
    
    };
        postorder(root);
        return ans;
    }
    void postorder(TreeNode* root) {
    
    
        if(root == nullptr) {
    
    
            return;
        }
        postorder(root -> left);
        postorder(root -> right);
        ans.push_back(root ->val);
    }
};

非递归

用栈便能模拟递归实现。其实就是体现dfs的思想。但是难度比递归大很多,必须分析过程,然后借助栈来表示,并没有什么很明显的规律。

前序遍历

在迭代算法中,思路演变成,每到一个节点 A,就应该立即访问它。

因为,每棵子树都先访问其根节点。对节点的左右子树来说,也一定是先访问根。

在 A 的两棵子树中,遍历完左子树后,再遍历右子树。

因此,在访问完根节点后,遍历左子树前,要将右子树压入栈。

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

中序遍历

每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。
在访问完 A 后,A 就可以出栈了。因为 A 和其左子树都已经访问完成。
具体思路见注释

class Solution {
    
    
    public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        if(root == nullptr) {
    
    
            return {
    
    };
        }
        stack<TreeNode*> s;
        vector<int> ans;
        TreeNode* cur = root;
        //一开始cur不入栈防止入栈两次
        while(cur || !s.empty()) {
    
    
            while(cur) {
    
    
                //将所有左子树入栈
                s.push(cur);
                cur = cur -> left;
            }
            //如果已经到了最左结点,将其入数组
            cur = s.top();
            s.pop();
            ans.push_back(cur -> val);
            //在根结点入数组的同时将根结点出栈,寻找根结点右边是否还有右子树
            //如果发现就能继续递归的过程,
            cur = cur -> right;
        }
        return ans;
    }
};

后序遍历

后序遍历的顺序是 左 右 根** ,而通过颠倒前序遍历的左右子树入栈顺序 ,我们能得到 根 右 左的顺序,
然后再将得到数组整个颠倒就能得到左 右 根。这是最好理解的,还有另一种解法可以参考大佬的题解

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

猜你喜欢

转载自blog.csdn.net/qq_42883222/article/details/114702722