前言
本文将主要围绕以下问题进行讨论:
二叉树的后序遍历。
本文将分别以两种方法【递归】【迭代】来解决这一问题(文章末尾附完整代码)
一、如何理解后序遍历?
二叉树的后序遍历是一种常见的遍历方式,主要按照 “左子树 - 右子树 - 根节点” 的顺序访问二叉树的每个节点。
二、方法一(递归法)
1.递归左右子节点
递归需要不断地重复调用同一函数,只改变传入节点,直到将所有节点全部遍历。
而在“改变传入节点”这一环节中,依照什么来改变呢?
我们可以通过每次传入当先节点的子节点,也就是先传左子节点,再传右子节点。在这个过程中用vector记录下这个顺序。
代码如下:
class Solution {
public:
vector<int> tre;
void digui(TreeNode* root){//左右根
if(root==nullptr){
return ;
}
if(root->left!=nullptr){
digui(root->left);
tre.push_back(root->left->val);
}
if(root->right!=nullptr){
digui(root->right);
tre.push_back(root->right->val);
}
}
vector<int> postorderTraversal(TreeNode* root) {
if(root==nullptr){
return tre;
}
else{
digui(root);
tre.push_back(root->val);
return tre;
}
}
};
值得注意的是:在所有的递归完成后,需要再将最初的根节点传进去。因为在这个逻辑下,我们是传入一个父节点下面的两个子节点,但是最初的根节点是没有父节点的,我们无法在递归中传入。因此只能最后在递归完成后,单独把最初根节点传进去。
2.递归根节点
相比于前一种递归方法来说,递归根节点就容易很多啦。
我们只需要先递归左,再递归右,传入节点本身就可以啦(依照左 - 右 - 根)
代码如下:
class Solution {
public:
vector<int> tre;
void digui(TreeNode* root) {
if (root == nullptr) return;
digui(root->left);
digui(root->right);
tre.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
digui(root);
return tre;
}
};
这种方法更加简明,同时也不需要在完成递归后,传入根节点补全向量。
三、方法二(迭代法)
这个迭代实现的后序遍历算法主要利用了栈来模拟递归的过程。
其核心思想是在遍历过程中,先尽可能地深入到左子树,当无法继续深入左子树时,检查当前节点的右子树是否已经被访问过,如果右子树未被访问,则转向右子树继续遍历;如果右子树已被访问或不存在右子树,则访问当前节点并将其从栈中弹出。
完整代码如下:(已标注好每句的作用)
/**
* 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 {
public:
vector<int> tre;//
stack<TreeNode*> sta;//建辅助栈
TreeNode* rot;//创建节点,用于后续
void diedai(TreeNode* root) {//创建迭代函数,传入节点,无返回值,因为我们是靠栈来存储的
if (root == nullptr){//如果是空树,直接跳出函数
return ;
}
while (root!= nullptr ||!sta.empty()) {//进入while循环
if (root!= nullptr) {//节点不为空,说明还没搜索到树的末端
sta.push(root);//将当前节点压入栈
root = root->left;//使节点指向左子节点
}//目的就是一直向树的左下方走
else {//说明此时root==nullptr,这时最左侧已经走到末端了
TreeNode* rooot = sta.top();//新建一个节点,用来取栈顶元素
if (rooot->right!= nullptr && rooot->right!=rot) {//如果这个栈顶元素的右侧还有节点,并且没有遍历过
root = rooot->right;//回溯,将节点指向右子节点
} else {//如果右侧无节点,或者已经遍历过
tre.push_back(rooot->val);//就直接把当前节点值送入vector中
rot = sta.top();//然后继续取下一个
sta.pop();//并且让它出栈
}
}
}
}
vector<int> postorderTraversal(TreeNode* root) {
diedai(root);
return tre;
}
};
时间复杂度为O(n)。