二叉树的深度优先遍历非递归深度解析

 如上图二叉树的遍历主要思想就是通过递归方式进行遍历,同时如果要非递归遍历的话,一般情况下,深度优先遍历需要借助stack保存中间变量的方式进行遍历,广度优先遍历的话需要借助queue来保存每一层变量的方式进行遍历。

对于深度优先遍历的递归的三种形式,不进行介绍,广度优先遍历,递归和非递归这篇文章也不进行介绍,这里就是想深刻的说下,深度优先遍历的三种非递归实现的原理。

先说下三种遍历的实现顺序吧

前序遍历:根,左,右

中序遍历:左,根,右

后续遍历:左,右,根

因为二叉树给到我们的是根结点:

所以我们应该先入栈这个根结点,然后可以入右,也可以入左,但是我们发现这个前序遍历是根左右,所以我们入左,然后入右,但是要注意这个入左,是一路进行递归的,直到左没有了,才会返回,然后进行入它上一级的右。而这个树的前序遍历也是,这样的,是根,然后访问左,然后访问左的左,也就是把整个树,理解成多个二叉树。下面画一个简单的图示。

 就是有这样一颗树,然后,如何进行对树入栈,出栈,我们给出示意图:

这里我们要明确一点,就是我们用栈来实现的遍历的过程,入栈和出栈是同时进行的,也就是说这个不是先都入完栈了,我们才进行出栈,这个对于初学者来说往往是一个比较大的误会。

所以我想用示意图,能够清晰的描述出这个入栈出栈的过程。我们把入栈的过程和出栈的过程先混合画,然后再分开来画。

我们按照先序的形式进行入栈,根 左 右

然后出栈顺序必然是 中序,左 根 右

上面的示意图,已经能够清晰的看到。

代码如下:我们想先序遍历,则是入栈前进行放在vector中,然后中序遍历就是出栈的时候放在       vector中。

/*思路
1、借用stack
2、在压最左边栈的同时需要把其节点也输出。
3、然后再把指针指向右边节点,然后在循环。
*/
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        if(!root){
            return result;
        }
        stack<TreeNode*> s;
        TreeNode *cur = root;
        while(!s.empty()|| cur){
            while(cur){
                result.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            /*退出则说明左边已经放完了,此时需要弹出栈顶元素,同时查看他的右节点,然后在继续循环*/
            cur = s.top();
            s.pop();
            cur = cur->right;
        }

        return result;
    }

中序非递归遍历的代码如下所示:

/*思路:
1、用栈的方式来处理,不用递归。
2、栈的思想就是,不需要提前把root节点压入栈,而是在循环中才压入。同时因为是中序遍历,所以最先压栈的是按理说是右,再是root,再是左。但是这里在循环过程中,需要从root开始判断压入栈的左子树是否为空,不为空则一直把左边节点压入栈,也就是按照树的最左边从root到业务节点压入栈。此时叶子节点左子树为空,则此时弹出stack top元素,写入result数组中,此时弹出的元素就是最左边的叶子节点数据。然后此时需要拿着这个节点看看他的右节点。如果不为空,则需要把他的右节点压栈,并且指针指向他右节点的左孩子。如果右节点为空,则继续弹出stack top元素,写入result中,此时弹出的则是数的左边那条线倒数第二个节点。如果右节点存在,则将右节点压入栈,同时下一个指针指向来右节点的左节点,如果左节点为空,则弹出stack top元素(此时弹出的就是这个右节点)。指针就系指向p->right。此时为空则继续弹出stack。。。。循环继续。直到stack为空&&p指针同时为空,则说明占用没有来数据同时把树已经遍历完成(p==nulptr)
*/
/*思路:非递归。
1、用一个栈,但是不会想广度优先算法一样先把root放在queue中
2、把左子树一直放在stack中,直到坐左边的节点
3、然后为空周,再把右子树放进去,循环判断。*/
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode *> s;
        if(!root){
            return result;
        }
        TreeNode *p = root;
        /*注意里面不需要临时节点指针了。直接用p就行了。*/
        while(!s.empty()||p){

            /*这里是把最左边全部压入栈,直到最左边的叶子节点*/
            while(p){
                s.push(p);
                p = p->left;
            }

            /*把左边全部压入栈,直到最左边的节点压完,此时弹出坐左边节点。*/
            p = s.top();
            s.pop();
            result.push_back(p->val);
            /*再看看这个最左边节点的右节点是否为空
            为空,继续弹出剩下的最左边,否则将右节点对应的最左边全部压入栈,然后在重复上面的循环*/
            p = p->right;
        }
        return result;
    }

对于后序遍历的非递归:

我们知道我们在入栈的时候是先序,那么出栈的时候,正好是中序。

那么我们想让后序遍历的话,我们可以从两个方向上下手。

因为现在入栈的顺序是 根 左 右(先序遍历),然后出栈的顺序,自然就是,左 右 根(中序遍历),而如果我们入栈的顺序,是 根 右 左,那么出栈的顺序自然是 右 左  根,但是这个右 左 根不是一种遍历形式,所以我们不会进行使用。

但是这里给了我们一种提示,如果我们把。 根 右 左,这种入栈形式保留在vector中,那么把这个保留的vector直接反转不就是我们的后序遍历非递归形式吗?   根 右 左  ------左 右 根

而这个根 右 左  出栈的顺序是右 左 根,是不符合要求的,但是我们对其进行操作,是不是也可以变成后序遍历的非递归形式呢?答案是可以的,下面分别从刚才分析的两个角度来解决问题。

1.从入栈的角度来分析,我们把入栈顺序改变,改为根,右,左

代码实现如下:

/*思路:
1、非递归方式通过stack,从右边一次放入stack,直到没有,则指针指向左子树,然后在循环放入栈,放入结果中,然后再把结果数组反序即可。*/
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        if(!root){
            return result;
        }
        stack<TreeNode*> s;
        TreeNode *cur = root;
        while(!s.empty()||cur){
            while(cur){
                result.push_back(cur->val);//输出
                s.push(cur);//压站
                cur = cur->right;
            }
            /*最右边压站完成,弹出顶元素,然后指向他的左孩子,继续循环压站*/
            cur = s.top();
            s.pop();
            cur = cur->left;
        }

        reverse(result.begin(),result.end());
        return result;
    }

2.就是从出栈的角度进行处理

这个方式后序进行补充,感觉第一种方式实现起来更加的简单。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图的深度优先遍历非递归的数据结构通常使用栈来保存中间变量。栈是一种后进先出(LIFO)的数据结构,它可以保存当前节点的信息以便后续处理。在深度优先遍历中,我们首先将起始节点入栈,然后依次将与当前节点相连的未访问过的节点入栈,直到没有未访问的相邻节点。此时,我们将栈顶节点出栈,并将其作为当前节点继续遍历。重复这个过程,直到栈为空为止。 具体实现时,我们可以使用一个辅助栈来保存未访问的节点。每次从栈中弹出一个节点时,我们将其标记为已访问,并将其未访问的相邻节点入栈。这样,我们就可以按照深度优先的顺序遍历图中的所有节点。 需要注意的是,为了避免重复遍历已经访问过的节点,我们需要在节点被访问时将其标记为已访问。这可以通过一个额外的布尔数组或哈希表来实现。 综上所述,图的深度优先遍历非递归的数据结构是栈。我们可以使用栈来保存未访问的节点,并按照深度优先的顺序遍历图中的所有节点。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [二叉树深度优先遍历非递归深度解析](https://blog.csdn.net/zhanghuaichao/article/details/124243582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值