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

前言

记录三十五 介绍了二叉树基础,和递归法模版及遍历方式;

递归:代码简单,但要想清楚三步:

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

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

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


一、"输入"学习阶段

学习参考链接

解释迭代

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

前序遍历

过程

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

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

代码实现

cpp 复制代码
/**
 * 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;
    }
};

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

cpp 复制代码
/**
 * 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结果。

代码实现

参考代码:

cpp 复制代码
/**
 * 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等于空,说明没有中节点可以取。

代码实现

  • 根据思路,先自我实现:

    cpp 复制代码
    /**
     * 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不为空时,入栈顺序:右左中。
cpp 复制代码
/**
 * 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不为空时,入栈顺序:右中左。
cpp 复制代码
/**
 * 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不为空时,入栈顺序:中右左。
cpp 复制代码
/**
 * 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,取到栈顶是空,代表要入栈。直到整个栈空。
    • 前序遍历:不为空,放入顺序:右左中;
    • 中序遍历:不为空,放入顺序:右中左;
    • 后序遍历:不为空,放入顺序:中右左;

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

相关推荐
葫三生1 小时前
如何评价《论三生原理》在科技界的地位?
人工智能·算法·机器学习·数学建模·量子计算
pipip.1 小时前
UDP————套接字socket
linux·网络·c++·网络协议·udp
拓端研究室3 小时前
视频讲解:门槛效应模型Threshold Effect分析数字金融指数与消费结构数据
前端·算法
随缘而动,随遇而安5 小时前
第八十八篇 大数据中的递归算法:从俄罗斯套娃到分布式计算的奇妙之旅
大数据·数据结构·算法
孞㐑¥5 小时前
Linux之Socket 编程 UDP
linux·服务器·c++·经验分享·笔记·网络协议·udp
IT古董5 小时前
【第二章:机器学习与神经网络概述】03.类算法理论与实践-(3)决策树分类器
神经网络·算法·机器学习
Alfred king8 小时前
面试150 生命游戏
leetcode·游戏·面试·数组
水木兰亭8 小时前
数据结构之——树及树的存储
数据结构·c++·学习·算法
June bug9 小时前
【软考中级·软件评测师】下午题·面向对象测试之架构考点全析:分层、分布式、微内核与事件驱动
经验分享·分布式·职场和发展·架构·学习方法·测试·软考
Jess079 小时前
插入排序的简单介绍
数据结构·算法·排序算法