数据结构:二叉树深度优先遍历的stack方法及三合一模板,C++实现

在二叉树的遍历中,前序遍历(Preorder)、中序遍历(Inorder)和后序遍历(Postorder)都可以使用栈来实现。下面是 C++ 实现这三种遍历方法的代码。

二叉树节点的定义

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

1. 前序遍历(Preorder Traversal)

前序遍历的顺序是:根节点 -> 左子树 -> 右子树。

#include <iostream>
#include <stack>

void preorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    std::stack<TreeNode*> stack;
    stack.push(root);

    while (!stack.empty()) {
        TreeNode* node = stack.top();
        stack.pop();

        std::cout << node->val << " ";  // 访问当前节点

        if (node->right) {
            stack.push(node->right);
        }
        if (node->left) {
            stack.push(node->left);
        }
    }
}

2. 中序遍历(Inorder Traversal)

中序遍历的顺序是:左子树 -> 根节点 -> 右子树。

#include <iostream>
#include <stack>

void inorderTraversal(TreeNode* root) {
    std::stack<TreeNode*> stack;
    TreeNode* curr = root;

    while (curr != nullptr || !stack.empty()) {
        while (curr != nullptr) {
            stack.push(curr);
            curr = curr->left;  // 移动到左子树
        }

        curr = stack.top();
        stack.pop();

        std::cout << curr->val << " ";  // 访问当前节点

        curr = curr->right;  // 移动到右子树
    }
}

3. 后序遍历(Postorder Traversal)

后序遍历的顺序是:左子树 -> 右子树 -> 根节点。使用栈实现后序遍历稍微复杂一些,通常使用两个栈来实现。

#include <iostream>
#include <stack>

void postorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    std::stack<TreeNode*> stack1, stack2;
    stack1.push(root);

    while (!stack1.empty()) {
        TreeNode* node = stack1.top();
        stack1.pop();
        stack2.push(node);

        if (node->left) {
            stack1.push(node->left);
        }
        if (node->right) {
            stack1.push(node->right);
        }
    }

    while (!stack2.empty()) {
        TreeNode* node = stack2.top();
        stack2.pop();
        std::cout << node->val << " ";  // 访问当前节点
    }
}

示例使用

假设你有如下二叉树:

    1
   / \
  2   3
 / \
4   5

你可以这样构造树并调用上述遍历方法:

int main() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    std::cout << "Preorder Traversal: ";
    preorderTraversal(root);
    std::cout << std::endl;

    std::cout << "Inorder Traversal: ";
    inorderTraversal(root);
    std::cout << std::endl;

    std::cout << "Postorder Traversal: ";
    postorderTraversal(root);
    std::cout << std::endl;

    return 0;
}

输出结果

运行上述代码时,输出将是:

Preorder Traversal: 1 2 4 5 3 
Inorder Traversal: 4 2 5 1 3 
Postorder Traversal: 4 5 2 3 1

解释

  1. 前序遍历:我们首先访问根节点,然后遍历左子树,最后遍历右子树。使用栈时,先将右子节点压入栈,再将左子节点压入栈。

  2. 中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树。使用栈时,我们不断向左下方向压入节点,当左子树遍历结束时,访问栈顶节点,再进入右子树。

  3. 后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点。这里使用两个栈,一个栈用于逆序收集节点,另一个栈用于记录最终遍历的顺序。

这种方法非常适合需要手动管理递归调用栈的场景,特别是在深度较深的二叉树中,栈的实现可以避免递归深度过大导致的问题。 

 前中后三合一模板

这个模板需要用到enum class(枚举类),enum class是c++11引入的一种枚举类型,称为“强类型枚举”或“枚举类”。与传统的 enum 相比,enum class 具有更严格的类型检查,并且避免了命名冲突。

模板函数实现

以下是使用一个模板函数来完成前序遍历、中序遍历和后序遍历的 C++ 代码:

#include <iostream>
#include <stack>
#include <functional>

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

enum class TraversalType {
    Preorder,
    Inorder,
    Postorder
};

void traverse(TreeNode* root, TraversalType type) {
    if (root == nullptr) {
        return;
    }

    std::stack<TreeNode*> stack;
    TreeNode* curr = root;
    TreeNode* lastVisited = nullptr;

    auto visit = [&](TreeNode* node) {
        std::cout << node->val << " ";
    };

    while (curr != nullptr || !stack.empty()) {
        if (curr != nullptr) {
            if (type == TraversalType::Preorder) {
                visit(curr);  // 前序遍历时,首先访问根节点
            }
            stack.push(curr);
            curr = curr->left;
        } else {
            TreeNode* node = stack.top();
            if (node->right != nullptr && lastVisited != node->right) {
                curr = node->right;
            } else {
                if (type == TraversalType::Inorder) {
                    visit(node);  // 中序遍历时,左子树处理完后访问根节点
                }
                stack.pop();
                if (type == TraversalType::Postorder) {
                    visit(node);  // 后序遍历时,最后访问根节点
                }
                lastVisited = node;
            }
        }
    }
}

int main() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    std::cout << "Preorder Traversal: ";
    traverse(root, TraversalType::Preorder);
    std::cout << std::endl;

    std::cout << "Inorder Traversal: ";
    traverse(root, TraversalType::Inorder);
    std::cout << std::endl;

    std::cout << "Postorder Traversal: ";
    traverse(root, TraversalType::Postorder);
    std::cout << std::endl;

    return 0;
}
解释
  • TraversalType 枚举:定义了遍历类型,有 Preorder(前序遍历)、Inorder(中序遍历)和 Postorder(后序遍历)。
  • 模板函数 traverse:函数根据 TraversalType 枚举值的不同,决定何时访问节点。
    • Preorder:在将节点压入栈前访问节点,顺序为根 -> 左 -> 右。
    • Inorder:在弹出节点后、处理完左子树后访问节点,顺序为左 -> 根 -> 右。
    • Postorder:在处理完左右子树后访问节点,顺序为左 -> 右 -> 根。
  • lastVisited:用于跟踪最后一个访问的节点,以便在后序遍历中处理右子树。
这样做的优点
  • 代码复用性强:一个模板函数处理三种遍历方式,减少了代码冗余。
  • 清晰易懂:通过一个枚举值选择遍历方式,使得代码逻辑清晰明确。
  • 扩展性好:可以根据需求增加其他遍历方式,只需修改 TraversalType 枚举和遍历逻辑即可。

这个模板方法简洁且功能强大,适用于需要支持多种遍历方式的场景。

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值