判断resultset是否遍历到最后一条记录_[LeetCode数据结构]二叉树的后序遍历

本文详细解析了二叉树的后序遍历算法,包括递归和非递归两种实现方式,重点讲解了非递归方法中的难点,如根节点处理和栈操作技巧。作者分享了从递归思路到非递归的转变过程,以及如何避免常见错误和优化算法以提高效率。
摘要由CSDN通过智能技术生成

0fa3b811bf28a5be1be9a8e6be9f08cd.png

题目描述

今天轮到后序遍历啦。(可恶的格式:)

2a52e96036d94c28f565eb5455861cc7.png

递归解法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> nodes;
        if(root != NULL){
            vector<int> left_tree = postorderTraversal(root->left);
            for(int i=0; i<left_tree.size(); i++){
                nodes.push_back(left_tree[i]);
            }
            
            vector<int> right_tree = postorderTraversal(root->right);
            for(int i=0; i<right_tree.size(); i++){
                nodes.push_back(right_tree[i]);
            }
            nodes.push_back(root->val);
        }
        return nodes;
    }
};

非递归写法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> nodes;
        stack<TreeNode*> st;
        TreeNode* T = root;
        TreeNode* last = NULL;
        while(T){
            st.push(T);
            T = T->left;
        }
        while(!st.empty()){
            T = st.top();
            st.pop();

            if(T->right == NULL || last == T->right){
                nodes.push_back(T->val);
                last = T;
            }
            else{
                st.push(T);
                T = T->right;
                while(T){
                    st.push(T);
                    T = T->left;
                }
            }
        }
        return nodes;
    }       
};

总结

非递归写法中,后序遍历是最难的一种,原因在于对待根节点(此处根节点是广义的,任何一个节点都可以认为是其子树的根节点,下同)的态度发生了变化。

在先序遍历和中序遍历中,碰到根节点直接访问即可,因为根节点并不是最后一个要被访问的。在非递归遍历中遇到根节点有两种情况:

  1. 由其父节点指向而得到(T = T->left; T为新的根节点);
  2. 在回溯的过程中通过出栈而得到(T = st.top(); T为之前保存在栈中的路径上的节点)

对第一种情况,后序遍历时,要一直追踪到左子树的左子树的左子树....也即左下角的节点(这一点跟中序遍历一样,先序遍历时先访问再追踪);

对第二种情况,后序遍历时,并不能马上访问根节点,而是要判断其右子树是否已经被访问过或者为空,满足二者之一才能访问,不然就得先去乖乖访问右子树;所以,需要记录上一次访问的节点是什么。体现在代码上,与之前的套路也有明显的不同。

======================================================

画外音:)

非递归写法我写了好久,最开始遇到的问题就是我还按照先序和中序的框架去判断,肯定是不对的。先序和中序,栈非空的判断和左子树的追踪是合二为一的,而后序要分开。

  1. 经常超时,原因是,访问完根节点后又把它压到栈里去了导致陷入死循环,这说明弹栈那段代码应该放在前面了,不能像先序和中序一样放在else条件里。

2. if那里,我最开始用的条件是(last == T->right || (last == T->left && T->right == NULL)).这样是不对的,相当于把条件加强了,last跟你此时此刻的T可能都不在一个分叉上了,凭什么认为上次访问的就是T的左子树?

3. while条件里面,只能判断栈空,若加上T非空指针的判断,则会导致栈空之后仍然会进入循环,导致栈中没有东西可弹而报错。为什么先序和中序可以那样写?因为它们俩栈空和T为空一定可以同时满足,而对于后序,最后栈空是因为把(真正的)根节点弹了出去,T一定非空,所以不同时满足,又导致死循环。仔细想想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值