二叉树的统一迭代法 标记法

我们以中序遍历为例,在二叉树:听说递归能做的,栈也能做! (opens new window)中提到说使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

方法一:就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法可以叫做空指针标记法

  1. 前序遍历(根 -> 左 -> 右):

    • 压栈顺序:右子节点 -> 左子节点 -> 当前节点 -> NULL
cpp 复制代码
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
                vector<int> result;
        stack<TreeNode*> st;
        if(root) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top( );
            if(node){
                st.pop();
                if(node->right) st.push(node->right);
                if(node->left) st.push(node->left);
                st.push(node);
                st.push(nullptr);
            }else{
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};
  1. 中序遍历(左 -> 根 -> 右):

    • 压栈顺序:右子节点 -> 当前节点 -> NULL -> 左子节点。
cpp 复制代码
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top( );
            if(node){
                st.pop();
                if(node->right) st.push(node->right);
                st.push(node);
                st.push(nullptr);
                if(node->left) st.push(node->left);
            }else{
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};
  1. 后序遍历(左 -> 右 -> 根):

    • 压栈顺序:当前节点 -> NULL -> 右子节点 -> 左子节点。
cpp 复制代码
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root) st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top( );
            if(node){
                st.pop();
                st.push(node);
                st.push(nullptr);
                if(node->right) st.push(node->right);
                if(node->left) st.push(node->left);
            }else{
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};

方法二:加一个 boolean 值跟随每个节点,false (默认值) 表示需要为该节点和它的左右儿子安排在栈中的位次,true 表示该节点的位次之前已经安排过了,可以收割节点了。 这种方法可以叫做boolean 标记法,样例代码见下文C++ 和 Python 的 boolean 标记法。 这种方法更容易理解,在面试中更容易写出来。

Boolean 标记法的核心思想

  1. 标记的作用

    • false:表示该节点需要被"安排"(即需要处理其子节点)。

    • true:表示该节点已经被"安排"过,可以直接收割(加入结果集)。

  2. 栈的使用

    • 栈中存储的是 pair<TreeNode*, bool>,其中 bool 表示节点的状态。
  3. 遍历顺序

    • 根据前序、中序、后序遍历的要求,调整节点的压栈顺序。

visited 是一个布尔值(bool),用于标记当前节点是否已经被"处理"过。它的作用是区分节点的两种状态:

  1. visited = false

    • 表示该节点 需要被安排,即需要处理其子节点。

    • 此时,节点会被重新压入栈中,并按照遍历顺序(前序、中序、后序)调整其子节点的压栈顺序。

  2. visited = true

    • 表示该节点 已经被安排过,可以直接"收割"(将其值加入结果集)。

    • 此时,节点不再需要处理其子节点。

cpp 复制代码
vector<int> traversal(TreeNode* root) {
    vector<int> result;
    if (!root) return result;

    stack<pair<TreeNode*, bool>> st;
    st.push({root, false}); // 初始状态为 false,表示需要安排子节点

    while (!st.empty()) {
        auto [node, visited] = st.top();
        st.pop();

        if (visited) {
            // 如果节点已经被访问过,直接收割
            result.push_back(node->val);
        } else {
            // 根据遍历顺序调整压栈顺序
            // 前序:根 -> 左 -> 右
            // 中序:左 -> 根 -> 右
            // 后序:左 -> 右 -> 根
            if (node->right) st.push({node->right, false}); // 右子节点
            st.push({node, true}); // 当前节点标记为已访问
            if (node->left) st.push({node->left, false});   // 左子节点
        }
    }

    return result;
}
相关推荐
字节源流几秒前
【SpringMVC】常用注解:@CookieValue
java·开发语言·servlet
DogDaoDao1 分钟前
Conda 虚拟环境创建:加不加 Python 版本的深度剖析
开发语言·人工智能·pytorch·python·深度学习·conda
不要小看我们之间的羁绊啊3 分钟前
SpringBoot入门-(1) Maven【概念+流程】
java
Java技术小馆7 分钟前
Java中的Fork/Join框架
java·后端·面试
贝克街的天才12 分钟前
Java最流行的分布式事务解决方案以及实际操作
java·后端
尤宸翎19 分钟前
Bash语言的语法
开发语言·后端·golang
xby123123xby22 分钟前
@Mapper、@Service以及@Component
java·开发语言
Ho1aAs24 分钟前
『Rust』Rust运行环境搭建
开发语言·后端·rust
奔跑吧邓邓子27 分钟前
【商城实战(37)】Spring Boot配置优化:解锁高效商城开发密码
java·spring boot·后端·配置优化·商城实战
阿黄学技术37 分钟前
Spring单例Bean的线程安全
java·后端·spring