leetcode 145. 二叉树的后序遍历的非递归实现

本文详细介绍了如何非递归地实现二叉树的后序遍历,包括使用两个栈的方法以及仅使用一个栈的两种不同实现方式。每个方法都通过具体代码示例进行了解释,帮助理解后序遍历的逻辑。此外,还提供了一个通用的模板,适用于前序和中序遍历的非递归实现。
摘要由CSDN通过智能技术生成

二叉树的后序遍历的非递归实现

对于二叉树的后序遍历可以以递归实现比较简单,也可以以迭代(非递归)实现。本文主要讲解如何实现二叉树后序遍历的非递归实现。

方法一:使用两个栈

对于二叉树遍历的非递归实现,一般都是要使用栈来进行作为辅助的空间。
申请两个栈s1, s2。后序遍历的步骤如下:

  1. 申请一个栈记为s1, 然后初始将根节点进行入栈s1
  2. 从s1中弹出结点记做为cur, 然后将cur的不为空的左右孩子进行入栈s1。将弹出的cur进行入栈s2。
  3. 不断重复上面的步骤1和步骤2,直到s1为空
  4. 从s2中弹出元素并打印,打印序列就是后序遍历的顺序。
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {       
        vector<int> res;
        if(!root) return res;
        stack<TreeNode*> s1;
        stack<TreeNode*> s2;
        s1.push(root);
        while(!s1.empty()){
            TreeNode* peek = s1.top();
            s1.pop();
            s2.push(peek);
            if(peek->left) s1.push(peek->left);
            if(peek->right) s1.push(peek->right);
        }
        while(!s2.empty()){
            res.push_back(s2.top()->val);
            s2.pop();
        }
        return res;
    }
};

方法二:使用一个栈

第一种实现:
本实现类似树的非递归的前序遍历,只是将左右孩子在入栈的时候的顺序进行调换一个,最后对收集的序列再进行逆置就行了。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) { 
	stack<TreeNode*> s;
	vector<int> res;
	if(root==NULL) return res;
	s.push(root);
	while(!s.empty()){
	    TreeNode* peek = s.top();
	    res.push_back(peek->val);
	    s.pop();
	    if(peek->left!=NULL) s.push(peek->left);
	    if(peek->right!=NULL) s.push(peek->right);
	}
	reverse(res.begin(), res.end());
	return res;
	    }
};

第二种实现:
上面的这个方法是借助一个栈进行实现,但是需要最终还需要逆置一下。下面的这个方法仍然是使用一个栈进行实现,但是不再需要最终再逆置。
维护两个变量,一个变量是cur表示的是最近一次弹出并打印的结点,peek表示栈的栈顶的结点,初始的时候cur为根结点,peek为空。

具体的步骤如下:

  1. 申请一个栈,将头结点压入栈中,维护两个变量,一个变量是cur表示的是最近一次弹出并打印的结点,peek表示栈的栈顶的结点,初始的时候cur为根结点,peek为空。

  2. 每次都令peek为栈顶的结点,但是不弹出,此时分为以下三种情况:

    -1.1如果peek的左孩子不为空,并且cur不等于peek的左孩子也不等于peek的右孩子,则把peek的左孩子压入栈中。

    -1.2如果情况一不成立,并且peek的右孩子不为空,并且cur不等于peek
    的右孩子,则把右孩子压入栈中
    -1.3 如果条件情况1和情况2都不成立,说明peek的左右孩子都已经打印完毕,那么就将当前的peek进行弹出打印即可。

  3. 重复步骤2,直到栈为空。

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

第三种实现
对于上面的这个使用一个栈的实现,其思路本质也是先不断将当前的左子树全部入栈,因此给出下面一种更加普适的非递归后序遍历,该方法也可以适用于前序和中序的非递归。主要思路如下:

  • 声明一个栈,初始的时候,栈中不存放元素,让cur指针指向当前遍历的结点,如果栈不为空,或者当前遍历的结点cur不为空的话,就一直将cur进行入栈,并且更新cur为cur的左孩子。否则如果当前的结点为空,但是栈非空,while循环仍然可以继续,那么说明最近一次遍历的节点h的所有的左孩子以及左子孙都已经入栈,那么取出当前的栈顶元素,就是最下方的左子孙,该左子孙的左孩子为空,判断他的右孩子,如果为空或者等于h,那么就说明h的左孩子和右孩子都已经遍历过,那么就该遍历当前的栈顶元素,并更新h为当前的栈顶元素,并出栈。否则更新当前的cur为cur的右孩子。
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {       
        vector<int> res;
        if(!root) return res;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        TreeNode* pre = NULL;//用来记录上一个已经结点
        while(!s.empty() || cur){
            while(cur){
                s.push(cur);
                cur = cur->left;
            }       
            cur = s.top();
            //下面的判断是说明,右子树当前的cur的右子树已经遍历完了,可以进行遍历当前的结点了
            if(!cur->right || cur->right==pre){
                res.push_back(cur->val);
                s.pop();
                pre = cur;
                cur = NULL;//这一步很重要,这一步能够使得在遍历完当前的cur以及其左右子树之后能够进一步往树的上方回溯
            }else{
                cur = cur->right;
            }
            
        }
        return res;
    }
};

因此主要的模板如下:

while(栈非空 || cur非空){//当cur为空但是,栈不为空的时候,其实就是往二叉树的上方进行回溯的过程
	if(cur 非空){
	}
	else{
	}
}

类似的前序遍历如下:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        //考虑使用非递归实现二叉树的中序遍历
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(!s.empty() || cur){
            while(cur){
                res.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            TreeNode* peek = s.top();
            s.pop();
            cur = peek->right;
        }
        return res;        
    }
};

类似的中序遍历的结果如下:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) { 
        vector<int> res;
        //考虑使用非递归实现二叉树的中序遍历
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while(!s.empty() || cur){
            while(cur){
                s.push(cur);
                cur = cur->left;
            }
            //此时已经把当前根节点的所有的左孩子都已经入栈
            TreeNode* peek = s.top();
            s.pop();
            //取出当前的栈顶元素
            res.push_back(peek->val);
            cur = peek->right;
        }
        return res;
    }
};

总结

综上,二叉树的前序、中序、后序的三种非递归实现,其实都可以使用普适的方法进行求解,其基本的思路都是将某一个未访问结点的左孩子不断入栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值