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

前言

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

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

  • 确定参数和返回值;
  • 确定终止条件,并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,取到栈顶是空,代表要入栈。直到整个栈空。
    • 前序遍历:不为空,放入顺序:右左中;
    • 中序遍历:不为空,放入顺序:右中左;
    • 后序遍历:不为空,放入顺序:中右左;

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

相关推荐
ZZZ_O^O3 分钟前
二分查找算法——寻找旋转排序数组中的最小值&点名
数据结构·c++·学习·算法·二叉树
CV-King26 分钟前
opencv实战项目(三十):使用傅里叶变换进行图像边缘检测
人工智能·opencv·算法·计算机视觉
代码雕刻家1 小时前
数据结构-3.9.栈在递归中的应用
c语言·数据结构·算法
雨中rain1 小时前
算法 | 位运算(哈希思想)
算法
小飞猪Jay2 小时前
C++面试速通宝典——13
jvm·c++·面试
Kalika0-02 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
代码雕刻家3 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
rjszcb3 小时前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想4 小时前
【C++】二叉搜索树
数据结构·c++