算法力扣刷题 三十六【二叉树迭代遍历】

前言

记录三十五 介绍了二叉树基础,和递归法模版及遍历方式;
递归:代码简单,但要想清楚三步:

  • 确定参数和返回值;
  • 确定终止条件,并return什么?;
  • 终止条件外的逻辑,在哪里做到自己调用自己?——应该是出现嵌套时候,要重复操作的时候。

递归的问题:递归次数多,开的新栈多,内存空间占用大。

如果二叉树前中后序遍历使用非递归方法,怎么操作?


一、“输入”学习阶段

学习参考链接

解释迭代

重复执行一段代码,直到满足某个条件后停止。用循环可以实现重复;(递归也是重复)。

前序遍历

过程

模拟递归过程,用结构栈。

  • 先把根节点放到栈中,再取出根节点加入遍历数组;
  • 先放入右孩子,再放入左孩子。(左孩子先出来,说明先处理左子树)。
  • 直到栈为空。
  • 总结:取出根节点,拿右孩子和左孩子入栈交换。先访问到中节点,可以直接处理中节点;再访问到左孩子(后入栈),处理左子树;再访问到右孩子(先入栈),处理右子树。访问节点顺序=处理节点顺序

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        st.push(root);
        while(!st.empty()){
            TreeNode* cur = st.top();
            st.pop();
            if(cur != nullptr){
                result.push_back(cur->val);
                st.push(cur->right);	//如果cur是叶子节点,把空也入栈,但是取到空的时候不操作。
                st.push(cur->left);
            }
        }
        return result;
    }
};

上面的实现:把叶子结点的左右孩子是空,也入栈,但是不操作。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root == nullptr) return result;
        st.push(root);
        while(!st.empty()){
            TreeNode* cur = st.top();
            st.pop();
            result.push_back(cur->val);
            if(cur->right) st.push(cur->right);
            if(cur->left) st.push(cur->left);
        }
        return result;
    }
};

这是参考代码,空就不入栈,所以可以在pop之后直接push_back。

后序遍历

过程

  • 前序实现得到:中左右;
  • 如果先放左孩子,再放右孩子;得到中右左;
  • 把result最后整体reverse,得到左右中。
  • 总结:先访问到中节点,可以直接处理中节点;再访问到右孩子(后入栈),处理右子树;再访问到左孩子(先入栈),处理左子树。访问节点顺序=处理节点顺序。最后reverse结果。

代码实现

参考代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode* > st;
        if(root == nullptr) return result;
        st.push(root);
        while(!st.empty()){
            TreeNode* cur = st.top();
            st.pop();
            result.push_back(cur->val);
            if(cur->left) st.push(cur->left);
            if(cur->right) st.push(cur->right);
        }
        reverse(result.begin(),result.end());
        return result;

    }
};

中序遍历

过程

有所区别原因:访问节点顺序不等于处理节点顺序。每一次先接触中间节点,但不能处理它,要找它的左子树;找到左子树,再处理它;再找右子树。

  • 用栈来记录遍历过的元素,虽然访问到但是还没到要处理的时候。
  • cur != 空,作为中节点,放入栈中;cur = cur->left;把left给到下一棒。
  • 如果遇到空,说明左子树结束;需要从栈中取元素,放入result,st.pop();cur = cur->right;再把right给到下一棒。
  • 当cur == 空且st栈为空,终止。cur==空,说明访问结束;st等于空,说明没有中节点可以取。

代码实现

  • 根据思路,先自我实现:
    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
     * };
     */
    class Solution {
    public:
        vector<int> inorderTraversal(TreeNode* root) {
            vector<int> result;
            stack<TreeNode*> st;
            if(root == nullptr) return result;
            st.push(root);
            TreeNode* cur = root->left;
            while(1){
                if(cur != nullptr){
                    st.push(cur);
                    cur = cur->left;
                }else{
                    if(st.empty()) break;
                    cur = st.top();
                    st.pop();
                    result.push_back(cur->val);
                    cur = cur->right;
    
                }
            }
            return result;
        }
    };
    
  • 对照参考代码:
    去掉 if(st.empty()) break; while条件改为(cur != nullptr || !st.empty())

二、统一迭代遍历实现

学习参考链接

思路学习

标记法

  • 访问的节点放入栈中,把要处理的节点也放入栈中但是标记要处理的节点。
  • 如何标记?要处理的节点放入栈之后,紧接着放入一个空指针作为标记

代码实现

  • 前序遍历:中左右。所以cur不为空时,入栈顺序:右左中。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root != nullptr) {st.push(root);}
        
        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != nullptr){
                st.pop();
                if(cur->right) st.push(cur->right);//先放入右孩子
                if(cur->left) st.push(cur->left); //再放入左孩子
                st.push(cur);   //放入中节点,紧跟着NULL,代表处理过,它下一轮该进result
                st.push(nullptr);
            }else{
                st.pop();   //把空指针先弹出
                TreeNode* temp = st.top();//获取入数组的元素
                st.pop();   //弹出该元素
                result.push_back(temp->val); 
            }

        }
        return result;
    }
};
  • 中序遍历:左中右。所以cur不为空时,入栈顺序:右中左。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root != nullptr) {st.push(root);}
        
        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != nullptr){
                st.pop();
                if(cur->right) st.push(cur->right);//先放入右孩子
                
                st.push(cur);   //放入中节点,紧跟着NULL,代表处理过,它下一轮该进result
                st.push(nullptr);
                if(cur->left) st.push(cur->left); //再放入左孩子
            }else{
                st.pop();   //把空指针先弹出
                TreeNode* temp = st.top();//获取入数组的元素
                st.pop();   //弹出该元素
                result.push_back(temp->val); 
            }

        }
        return result;
    }
};
  • 后序遍历:左右中。所以cur不为空时,入栈顺序:中右左。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root != nullptr) st.push(root);
        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != nullptr){
                st.push(nullptr);	//先放中节点,直接放null,标记下
                if(cur->right) st.push(cur->right);//再放右孩子
                if(cur->left) st.push(cur->left);//再放左孩子
            }else{
                st.pop();
                TreeNode* temp = st.top();
                st.pop();
                result.push_back(temp->val);
            }
        }
        return result;
    }
};

总结

本文学习迭代遍历二叉树。用循环+栈处理。

  • 非统一的方法:
    • 前序遍历:根节点先入栈,每出栈一个根节点,就把右左孩子放入。相当于交换。
    • 后序遍历:中左右——中右左——左右中;
    • 中序遍历:栈放遍历的元素,不为空,把left交到下一棒;为空,取出中节点;把right交到下一棒。
  • 统一方法:标记法。要处理的节点后面紧跟着是null,取到栈顶是空,代表要入栈。直到整个栈空。
    • 前序遍历:不为空,放入顺序:右左中;
    • 中序遍历:不为空,放入顺序:右中左;
    • 后序遍历:不为空,放入顺序:中右左;

(欢迎指正,转载标明出处)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值