二叉树三种遍历(迭代、递归)

二叉树三种遍历(迭代、递归)文章目录

1.迭代版本
1)先序遍历
方法一(较简单)
方法二(通用法)
方法二修改版
2)中序遍历
方法一
先序中序对比
方法二
3)后序遍历
方法一
方法二(辅助标记)
方法二
3. 递归版本

1.迭代版本

1)先序遍历
方法一(较简单)
访问栈顶节点,并将右子树、左子树按顺序入栈(先右再左,左子树比右子树先出栈)。左子树访问完才会访问右子树,各节点按照访问顺序分别入栈,转到第一步。
PS:这种方法是由递归版本消除尾递归得到,不具有一般性。
简化:
while(栈非空){
栈顶出栈、访问->右子树进栈->左子树进栈
}
【TIP】空节点入栈和不入栈分别有一种写法

//左右子树都入栈,空节点不入栈
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
       vector<int> res;
        if(!root) return res;
        stack<TreeNode*> Stack;
        Stack.push(root);
        while(!Stack.empty())
        {
            TreeNode* curNode=Stack.top();
            res.push_back(curNode->val);
            Stack.pop();
            if(curNode->right)
                Stack.push(curNode->right);
            if(curNode->left)
                Stack.push(curNode->left);
        }
        return res;
    }
};
//另一种写法,空节点入栈,可以处理空节点
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
       vector<int> res;
        if(!root) return res;
        stack<TreeNode*> Stack;
        Stack.push(root);
        while(!Stack.empty())
        {
            TreeNode* curNode=Stack.top();
            res.push_back(curNode->val);
            Stack.pop();
            if(curNode==NULL)
            	continue;
            Stack.push(curNode->right);
            Stack.push(curNode->left);
        }
        return res;
    }
};

方法二(通用法)

内循环:访问当前节点并将右子树入栈,往左子树方向一直访问下去。左子树访问到底退出循环,进入外循环。
外循环:将栈顶右子树取出,转到上一步。
在这里插入图片描述
因为左链已经访问完毕,所以只需将右子树入栈。

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> S;
        vector<int> v;
        TreeNode* rt = root;
        while(rt || S.size()){
            while(rt){
                S.push(rt->right); //NULL有可能入栈,但并不影响
                v.push_back(rt->val);
                rt=rt->left;
            }
            rt=S.top();
            S.pop();
        }
        return v;        
    }
}

方法二修改版

用左链代替右子树入栈,出栈时,用节点右子树代替出栈节点。
在这里插入图片描述

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> S;
        vector<int> v;
        TreeNode* rt = root;
        while(rt || S.size()){
            while(rt){
                S.push(rt); 
                v.push_back(rt->val);
                rt=rt->left;
            }
            rt=S.top();
            S.pop();
            rt=rt->right;//rt可能为NULL,但并不影响
        }
        return v;    
}

**

2)中序遍历 方法一

**
在这里插入图片描述
因为中序遍历得先访问左节点,所以沿左侧分支寻找最左节点时并没有访问根节点。因此,需要将根节点入栈(后进先出),出栈时同时访问右节点(将右节点设为根节点,完成此子任务)。
PS:由先序遍历方法二第二个版本推广得到,只有几行有变化。

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> S;
        vector<int> v;
        TreeNode* rt = root;
        while(rt || S.size()){
            while(rt){
                S.push(rt);
                rt=rt->left;
            }
            rt=S.top();
            S.pop();
            v.push_back(rt->val);
            rt=rt->right; //转向右子树(可能为空,但不影响)
        }
        return v;        
    }

先序中序对比`

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> S;
        vector<int> v;
        TreeNode* rt = root;
        while(rt || S.size()){
            while(rt){
                S.push(rt);
 	 //先序遍历:v.push_back(rt->val);
                rt=rt->left;
            }
            rt=S.top();
            S.pop();
 //中序遍历:v.push_back(rt->val);
            rt=rt->right; //转向右子树(可能为空,但不影响)
        }
        return v;        
    }

方法二

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s1;
        vector<int> res;
        if(root!=NULL){
            while(root!=NULL || !s1.empty()){
                if(root!=NULL){
                    s1.push(root);
                    root=root->left;
                }
                else{
                    root=s1.top();
                    res.push_back(root->val);
                    s1.pop();
                    root=root->right;
                }
            }
        }
        return res;
    }
};

3)后序遍历 方法一

将前序遍历的中左右变为中右左,然后再将中右左反向!
反向即为先进后出,用第二个栈实现!!

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> s1;
        stack<TreeNode*> s2;
        vector<int> res;
        if(!root) return res;
        s1.push(root);
        while(!s1.empty()){
            root=s1.top();
            s1.pop();
            s2.push(root);
            if(root->left)
                s1.push(root->left);
            if(root->right)
                s1.push(root->right); 
        }
        while(!s2.empty()){
            res.push_back(s2.top()->val);
            s2.pop();
        }
        return res;
    }
};

方法二(辅助标记)

由中序遍历推广得到,前半部分几乎一样。
不同点在于:沿左链入栈到左链最低点后,并没有直接弹出栈顶,而是
先判断:
该节点是否有右节点 或者 右节点是否访问完毕

若右节点访问完毕 或者 无右节点,才开始访问操作:
弹出并访问该节点,并把该节点赋值给pre(辅助标记,表示前一个被访问的节点),令节点为空,开始对下一个栈顶的判断。

若右节点没有被访问(pre不是右节点):将右节点独立为子任务,遍历该子任务。

class Solution {
public:
        stack<TreeNode*> mystack;
        vector<int> ans;
        TreeNode* curr = root;
        TreeNode* pre = NULL;//辅助标记:前一个被访问的节点
        while(curr || !mystack.empty())
        {
            while(curr)
            {
                mystack.push(curr);
                curr = curr->left;
            }
            curr = mystack.top();
            //若前一个访问的是右节点(已访问过)或者没有右节点,则访问该节点
            if(!curr->right || pre == curr->right){
                mystack.pop();
                ans.push_back(curr->val);    
                pre = curr;//将该节点设为前一个访问的节点
                curr = NULL;
            }else{//右节点还没有被访问,则将右节点独立为子任务进行遍历
                curr = curr->right;
                pre = NULL;
            }
        }
        return ans;
}

方法二
将前序遍历中的left、right相互替换,结果再逆序输出,即为后序遍历结果。但本质上并不是后序遍历。类似的方法还有很多。

3. 递归版本

根据遍历顺序,分别把遍历函数放在左右子树递归语句前中后即可

//前序:
class Solution {
public:
    vector<int> vec;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root==NULL) return vec;
        vec.push_back(root->val);
        if(root->left) preorderTraversal(root->left);
        if(root->right) preorderTraversal(root->right);
        return vec;
    }
};

//中序:
 class Solution {
public:
    vector<int> vec;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root==NULL) return vec;
        if(root->left) preorderTraversal(root->left);
        vec.push_back(root->val);
        if(root->right) preorderTraversal(root->right);
        return vec;
    }
};

//后序:
 class Solution {
public:
    vector<int> vec;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root==NULL) return vec;
        if(root->left) preorderTraversal(root->left);
        if(root->right) preorderTraversal(root->right);
        vec.push_back(root->val);
        return vec;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值