https://leetcode.com/problems/binary-tree-preorder-traversal/
https://leetcode.com/problems/binary-tree-inorder-traversal/
https://leetcode.com/problems/binary-tree-postorder-traversal/
算法思路:简单递归,或者采用栈模拟实现迭代,由于三者递归路径一样,只是访问(打印)顺序不一致,所以根据定义可以整理出相同模板的非递归方法。
节点定义如下,非递归算法的堆栈模拟原理图在博客最下方。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
前序遍历(先序遍历)的递归算法:1、访问根节点 2、前序遍历其左子树 3、前序遍历其右子树 记为:根左右
//前序遍历递归方法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
preorder(root,res);
return res;
}
private:
void preorder(TreeNode* node,vector<int>& res){
if(node){
res.push_back(node->val);
preorder(node->left,res);
preorder(node->right,res);
}
}
};
前序遍历的的非递归算法:使用栈来模拟前序访问过程,前序遍历的访问顺序为根左右,具体为
- 遇到一个结点,先访问这个结点,然后把它压栈,并去遍历它的左子树;
- 当左子树遍历结束后,从栈顶弹出这个结点;
- 然后按其右指针再去前序遍历该结点的右子树。
//前序遍历非递归方法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
preorder(root,res);
return res;
}
private:
void preorder(TreeNode* node,vector<int>& res){
stack<TreeNode*> stk;
while(node || !stk.empty()){
while(node){
res.push_back(node->val);
stk.push(node);
node=node->left;
}
if(!stk.empty()){
node=stk.top();
stk.pop();
node=node->right;
}
}
}
};
中序遍历的递归算法:1、中序遍历左子树 2、访问根节点 3、中序遍历右子树 记为:左根右
//中序遍历递归方法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root,res);
return res;
}
private:
void inorder(TreeNode* node,vector<int>& res){
if(node){
inorder(node->left,res);
res.push_back(node->val);
inorder(node->right,res);
}
}
};
中序遍历的非递归算法:使用栈模拟中序访问过程,中序访问过程为左根右,具体为
- 遇到一个结点,就然后把它压栈,并去遍历它的左子树;
- 当左子树遍历结束后,从栈顶弹出这个结点并访问它;
- 然后按其右指针再去中序遍历该结点的右子树。
//中序遍历递非归方法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root,res);
return res;
}
private:
void inorder(TreeNode* node,vector<int>& res){
stack<TreeNode*> stk;
while(node || !stk.empty()){
while(node){
stk.push(node);
node=node->left;
}
if(!stk.empty()){
node=stk.top();
stk.pop();
res.push_back(node->val);
node=node->right;
}
}
}
};
后序遍历的递归算法: 1、后序遍历其左子树 2、后序遍历其右子树 3、访问根节点 记为:左右根
//后序遍历递归方法
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
postorder(root,res);
return res;
}
private:
void postorder(TreeNode* node,vector<int>& res){
if(node){
postorder(node->left,res);
postorder(node->right,res);
res.push_back(node->val);
}
}
};
后序遍历的非递归算法: 使用堆栈来模拟后序遍历的访问过程,后序遍历的访问过程为左右跟,具体为
- 遇到一个结点,就然后把它压栈,并去遍历它的左子树;
- 当左子树遍历结束后,从栈顶弹出这个结点;
- 如果弹出结点的右子树为空或者弹出结点的右子结点是上一个弹出结点,那么访问该结点,并且把prePop置为当前结点,然把当前结点置为NULL;
- 否则,按其右指针再去后序遍历该结点的右子树。
后续遍历相对于前序和中序遍历,在使用堆栈的时候,子树跟结点最多需要两次压入,有的虽然不是经过两次压入,但都是在第三次碰到的时候在进行访问,第三次的碰到并不是通过计数的方式,因为一棵树的最左结点,第一次第二次第三次碰到都是同一时刻,所以使用的判定条件是该结点右结点为空或者右结点是上一个被弹出访问的结点。具体结合博客最后面的图进行对照理解。
//以下两种方法都可,推荐第一种,因为风格和前面更接近,方便记忆
//后序遍历非递归方法1
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
postorder(root,res);
return res;
}
private:
void postorder(TreeNode* node,vector<int>& res){
stack<TreeNode*> stk;
TreeNode *prePop=NULL;
while(node || !stk.empty()){
while(node){
stk.push(node);
node=node->left;
}
while(!stk.empty()){
node=stk.top();
stk.pop();
if( node->right==NULL || node->right==prePop){
res.push_back(node->val);
prePop=node;
node=NULL; //不能省去,比如最简单的一个树,只有一个结点,保存完因栈空跳出循环,
//在大循环因为node没有置空,又重新进行循环导致无线循环最终TLE
}else{
stk.push(node);
node=node->right;
break;
}
}
}
}
};
//后序遍历非递归方法2
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
postorder(root,res);
return res;
}
private:
void postorder(TreeNode* node,vector<int>& res){
stack<TreeNode*> stk;
TreeNode *prePop=NULL;
while(node || !stk.empty()){
while(node){
stk.push(node);
node=node->left;
}
node=stk.top();
stk.pop();
if( node->right==NULL || node->right==prePop){
res.push_back(node->val);
prePop=node;
node=NULL;
}else{
stk.push(node);
node=node->right;
}
}
}
};