总思路
首先树的遍历一共有三种
- 前序遍历
顺序是 根结点 - 左子树 - 右子树 - 后序遍历
顺序是 左子树 - 右子树 - 根结点 - 中序遍历
顺序是 左子树 - 根结点 - 右子树
并且对于每个结点都按照一样的顺序递归执行
递归
递归方式下,大家都很熟悉,无非就是函数体内根据遍历的顺序来调用。
void preorder(TreeNode* root) {
if(root == nullptr) {
return;
}
ans.push_back(root ->val);
preorder(root -> left);
preorder(root -> right);
}
按照这样的格式修改顺序即可,如果想要知道这样为什么可以实现,可以在懂得分析递归过程的前提下自己走一遍就知道了,就是扩展,回溯的过程。如果还不会分析递归过程,那不要硬记,去多学学递归再做。
递归实现:
前序遍历
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);
}
};
中序遍历
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);
}
};
后序遍历
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;
}
};