二叉树经典题之二叉树的非递归遍历

Posted 快乐江湖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树经典题之二叉树的非递归遍历相关的知识,希望对你有一定的参考价值。

前序遍历非递归

题目链接:LeetCode:前序遍历非递归

思路

让左节点不断入栈,入栈就代表访问(在题目中对应的就是将元素压入vector中),当走到最左侧时停止入栈,此时现在的任务就是处理右子树了。所以开始出栈,每出栈一个元素查看其是否存在右子树,如果没有右子树那么继续出栈,如果存在右子树,这表示来了一个新的子树,那么对于这颗子树只需作为子问题重复执行即可

代码

 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        stack<TreeNode*> s;
        TreeNode* cur=root;
        while(cur || !s.empty())
        {
            //首先让这颗树的左节点一直入栈
            while(cur)
            {
                ret.push_back(cur->val);
                s.push(cur);
                cur=cur->left;
            }
            //当停止时表示到了这棵树的最左结点,那么此时出栈看一下是否存在右子树
            //如果不存在子树那么cur就是null,也就是会继续出下一个结点
            //如果存在子树,那么cur就会作为子问题去处理其右子树
            TreeNode* temp=s.top();
            s.pop();
            
            cur=temp->right;
            
        }
        return ret;
    }
};

其他写法

关于前序遍历其实还有另外一种比较流行的写法,但是这种写法不推荐使用,因为它和我们上面的思路有点不相符合,掌握上面的思路后也可以适用于后序和中序遍历的非递归

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        stack<TreeNode*> s;
        if(root)
        {
            s.push(root);
            while(!s.empty())
            {
                TreeNode* p=s.top();
                s.pop();
                ret.push_back(p->val);//前序遍历进栈时做访问结点的操作
                
                if(p->right)//先入右节点,因为先序遍历先要访问左节点
                {
                    s.push(p->right);
                }
                if(p->left)
                {
                    s.push(p->left);
                }
            }
        }
        return ret;
    }
};

中序遍历非递归

题目链接:LeetCode:中序遍历非递归

思路

依旧采用前序遍历非递归的第一种思路,代码也仅仅有很小的区别。在中序遍历非递归中,要一直先进栈,但进栈的时候不做访问结点的操作,一直到最左面结点时出栈,出栈时访问,访问完毕之后再看其是否有子树,如果有的话继续处理其子树,也就是子问题

代码

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        stack<TreeNode*> s;
        TreeNode* cur=root;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            
            TreeNode* temp=s.top();
            s.pop();
            ret.push_back(temp->val);//中序遍历出栈时做访问结点的操作
            
            cur=temp->right;
        }
        return ret;
    }
};

后序遍历非递归

题目链接:LeetCode:后序遍历非递归

思路

后序遍历有一些细节需要注意,后序遍历由于是最后才访问根节点,所以根节点势必会经过两次,第一次经过时是判断其是否拥有右子树,如果有的话,第二次经过时显然是右子树已经访问完毕了,所以在这里必须要进行合理的判断,否则就会出现死循环

此题中可以定义一个指针prev,在准备pop时,看一下当前栈顶结点的右节点是否被访问过

代码

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root)
    {
        vector<int> ret;
        stack<TreeNode*> s;
        TreeNode* cur=root;
        TreeNode* prev=nullptr;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            
            TreeNode* temp=s.top();
            //拿到结点后先不要出栈,需要进行判断
            //如果右子树为空,或者说取出的这个节点的右子树已经访问完毕了,那么就访问该根节点
            if(temp->right==nullptr || temp->right==prev)
            {
                ret.push_back(temp->val);
                s.pop();
                
                prev=temp;//该根节点有可能还是其他结点的右节点
                cur=nullptr;
            }
            else
            {
                //如果右子树不为空,或者没有访问,那么继续子问题
                cur=temp->right;
            }
        }
        return ret;
    }
};

更为简单的写法

上面的写法只是为了很好的理解后序遍历,其实我们只需对前序遍历非递归稍加改动即可完成后序遍历的非递归写法

后序遍历是“左-右-根”,而前序遍历是“根-左-右”,如果将前序遍历变为“根-右-左”,然后反转岂不是就达到了目的?其实“根-右-左”这种遍历方式我们称之为逆后序遍历。所以这也就意味着只需将前序遍历改为先右后左,然后再反转vector即可

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        stack<TreeNode*> s;
        TreeNode* cur=root;
        while(cur || !s.empty())
        {
            while(cur)
            {
                ret.push_back(cur->val);
                s.push(cur);
                cur=cur->right;//先右
            }
            TreeNode* temp=s.top();
            s.pop();
            
            cur=temp->left;//后左
            
        }
        reverse(ret.begin(),ret.end());//反转
        return ret;
    }
};

在这里插入图片描述

以上是关于二叉树经典题之二叉树的非递归遍历的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode刷题之二叉树的前序遍历

数据结构之二叉树详解

二叉树经典题之二叉树最近公共祖先(LeetCode)

二叉树的非递归遍历

二叉树经典题之根据二叉树创建字符串(二叉树的括号表示法)

二叉树的非递归遍历怎么写?