题目:
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [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(相当于是出栈顺序),因此入栈时应该右子结点先入栈,左子结点后入栈。