二叉树的遍历

二叉树的遍历一般两种,深搜,广搜。

广搜非常简单,只需要一个队列就行了,只要左右孩子不为空,那么就入队,代码就不放了();

主要写一下深搜,也就是先序遍历,中序遍历,后序遍历。

三者的区别在于什么时候输出中间元素,也就是头。

递归版本

(代码展示很抽象,但理解意思即可)

void preOrder(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    cout << head->val << " ";
    preOrder(head->left);
    preOrder(head->right);
}

// 中序打印所有节点,递归版
void inOrder(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    inOrder(head->left);
    cout << head->val << " ";
    inOrder(head->right);
}

// 后序打印所有节点,递归版
void posOrder(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    posOrder(head->left);
    posOrder(head->right);
    cout << head->val << " ";
}

不用指针也可以的,可以把0当作空指针,判断是不是0就行了。

迭代版本(难一些,但是用时更少)

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};

// 【先序遍历】迭代版
// 顺序:中 -> 左 -> 右
void preOrderUnRecur(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    cout << "先序遍历迭代版: ";
    
    stack<TreeNode*> st;
    st.push(head); // 1. 先把头节点放入

    while (!st.empty()) {
        // 2. 弹出并打印
        TreeNode* cur = st.top();
        st.pop();
        cout << cur->val << " ";

        // 3. 有右先压右(这样左边后压,就能先弹出来)
        if (cur->right != nullptr) {
            st.push(cur->right);
        }
        // 4. 有左再压左
        if (cur->left != nullptr) {
            st.push(cur->left);
        }
    }
    cout << endl;
}

// 【中序遍历】迭代版
// 顺序:左 -> 中 -> 右
// 逻辑:每棵子树,都要把左边界一股脑压入栈,压到底后弹出打印,然后去右树
void inOrderUnRecur(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    cout << "中序遍历迭代版: ";
    
    stack<TreeNode*> st;
    TreeNode* cur = head;

    // 当栈不为空,或者当前节点不为空时,继续循环
    while (!st.empty() || cur != nullptr) {
        if (cur != nullptr) {
            // 1. 只要当前节点有东西,就入栈,并且一直往左走
            st.push(cur);
            cur = cur->left;
        } else {
            // 2. 左边走到头了(cur == nullptr),弹出栈顶节点并打印
            cur = st.top();
            st.pop();
            cout << cur->val << " ";
            
            // 3. 往右走一步,然后继续下一轮循环(下一轮又会尝试往左走)
            cur = cur->right;
        }
    }
    cout << endl;
}

后序遍历有两种办法,一种是两个栈实现,一种是一个栈实现。

两个栈实现其实就是将一个栈作为输出的工具,因为输出顺序是“左右中”,那么压入顺序就是“中右左”,那么处理的顺序就是“中右左”,所以压入第一个栈的顺序就是“左右中”,

// 【后序遍历】迭代版 (双栈法 - 最容易理解的写法)
// 顺序:左 -> 右 -> 中
// 逻辑:先序是 中左右。如果我们将先序逻辑改成 中右左,
// 放到收集栈里,最后收集栈倒出来就是 左右中。
void posOrderUnRecur(TreeNode* head) {
    if (head == nullptr) {
        return;
    }
    cout << "后序遍历迭代版: ";

    stack<TreeNode*> s1; // 遍历用的栈
    stack<TreeNode*> s2; // 收集结果的栈
    
    s1.push(head);
    
    while (!s1.empty()) {
        TreeNode* cur = s1.top();
        s1.pop();
        
        // 这里的顺序是:中 -> s2
        s2.push(cur);
        
        // 这里的入栈顺序是先左后右
        // s1弹出时就是:先右后左
        // 结合上面的s2 push,s2里的顺序就是:中、右、左
        if (cur->left != nullptr) {
            s1.push(cur->left);
        }
        if (cur->right != nullptr) {
            s1.push(cur->right);
        }
    }

    // s2 现在的顺序(从底到顶)是:中 -> 右 -> 左
    // 依次弹出打印,就是:左 -> 右 -> 中
    while (!s2.empty()) {
        cout << s2.top()->val << " ";
        s2.pop();
    }
    cout << endl;
}

这个版本非常好理解,就是先序遍历稍微改一下,但是还有一种只用一个栈的方法。

// 后序打印所有节点,非递归版
// 这是用一个栈的方法
void posOrderOneStack(TreeNode* h) {
    if (h != nullptr) {
        stack<TreeNode*> st;
        st.push(h);
        
        // 如果始终没有打印过节点,h就一直是头节点
        // 一旦打印过节点,h就变成打印节点
        // 之后h的含义 : 上一次打印的节点
        while (!st.empty()) {
            TreeNode* cur = st.top();
            
            // 这里的逻辑稍微比较绕,解释一下:
            // 1. h != cur->left  : 说明 h 不是 cur 的左孩子(即:左子树还没处理完,或者刚处理完回来)
            // 2. h != cur->right : 说明 h 不是 cur 的右孩子(即:右子树还没处理完)
            // 结合起来:如果 cur 有左孩子,且既不是刚从左边回来,也不是刚从右边回来 -> 那就往左走
            
            if (cur->left != nullptr && h != cur->left && h != cur->right) {
                // 有左树且左树没处理过
                st.push(cur->left);
            } 
            else if (cur->right != nullptr && h != cur->right) {
                // 有右树且右树没处理过
                // (代码走到这里隐含了:左树为空 或者 左树已经处理完了即 h == cur->left)
                st.push(cur->right);
            } 
            else {
                // 左树、右树 没有 或者 都处理过了
                cout << cur->val << " ";
                
                // C++ pop() 不返回值,所以分两步写
                h = st.top(); 
                st.pop();
            }
        }
        cout << endl;
    }
}

再见

posted @ 2026-03-01 17:14  Lambda_L  阅读(3)  评论(0)    收藏  举报