LeetCode_145.二叉树的后序遍历

题目:

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-postorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法一:递归法

时间复杂度: O(n) //每个结点遍历一遍
空间复杂度: O(n) //递归要借助栈

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    
    
private:
    vector<int> result;
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        if(root == nullptr) return result;
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        result.push_back(root->val);
        return result;        
    }
};

解法二:迭代法:调整DFS

正常的DFS深度优先遍历一棵二叉树的结果是按照先序(根左右)的顺序输出(右子结点作为回溯点),如果对其进行调整, 使左子结点作为回溯点,那么遍历后的输出结果就是“根右左”,再对结果vector翻转(reverse),得到的最终结果就是“左右根”,即后序。

这种方法的好处是写法简单,DFS深度优先遍历即可。缺点是时间复杂度过高,因为要在原有的迭代遍历的基础上再加一步reverse反转数组,时间复杂度相当于编程了O(2n)。

时间复杂度: O(n) //O(2n)
空间复杂度: O(n)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
 //深度优先遍历
class Solution {
    
    
private:
    vector<int> result;
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> s;
        if(root != nullptr) s.push(root);
        while(!s.empty()) {
    
    
            TreeNode* cur = s.top();
            s.pop();
            result.push_back(cur->val);
            if(cur->left != nullptr) s.push(cur->left); //左子结点作为回溯点
            if(cur->right != nullptr) s.push(cur->right);
        }
        reverse(result.begin(), result.end()); //反转数组
        return result;
    }
};

方法三:迭代法

解题思路:

后序遍历(左右根)的顺序,是要左右子结点都访问到了之后才访问根结点,访问顺序也就是将结点的值(val)插入到vector(result)中的顺序。因此在将根节点插入前需要判断它的左右子结点是否都已插入到vector中。

后序遍历的迭代法同样需要借助栈:
何时入栈:遍历到这个结点且它的左右子结点中至少有一个还没有插入到vector之中;
何如出栈:结点的左右子结点都已插入到vector之中。

== 这种解题方法的关键点在于使用一个set记录结点是否已被访问到,以此达到左右子结点都访问到之后才访问根节点的目的。 ==

时间复杂度: O(n) //每个结点遍历一次
空间复杂度: O(n) //栈

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    
    
private:
    vector<int> result;
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> s;
        set<TreeNode*> record; //用于记录结点是否已被访问过
        if(root != nullptr) s.push(root);
        while(!s.empty()) {
    
    
            TreeNode* cur = s.top();
            bool bothChildVisited = true;
            if(cur->right != nullptr && record.count(cur->right) == 0) {
    
    
                s.push(cur->right);
                bothChildVisited = false;
            }
            if(cur->left != nullptr && record.count(cur->left) == 0) {
    
    
                s.push(cur->left);
                bothChildVisited = false;
            }
            if(bothChildVisited == true) {
    
    
                result.push_back(cur->val);
                record.insert(cur); //插入到result中即需记录此结点已被访问到
                s.pop();
            }
        }
        return result;
    }
};

Tips:
为什么要先 cur->right 右子结点入栈,后 cur->left 左子结点入栈?

遍历顺序要求是左右根,即左子结点先插入vector,右子结点后插入vector(相当于是出栈顺序),因此入栈时应该右子结点先入栈,左子结点后入栈。

猜你喜欢

转载自blog.csdn.net/ArtAndLife/article/details/109009387