在二叉树的遍历中,前序遍历(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
解释
-
前序遍历:我们首先访问根节点,然后遍历左子树,最后遍历右子树。使用栈时,先将右子节点压入栈,再将左子节点压入栈。
-
中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树。使用栈时,我们不断向左下方向压入节点,当左子树遍历结束时,访问栈顶节点,再进入右子树。
-
后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点。这里使用两个栈,一个栈用于逆序收集节点,另一个栈用于记录最终遍历的顺序。
这种方法非常适合需要手动管理递归调用栈的场景,特别是在深度较深的二叉树中,栈的实现可以避免递归深度过大导致的问题。
前中后三合一模板
这个模板需要用到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
枚举和遍历逻辑即可。
这个模板方法简洁且功能强大,适用于需要支持多种遍历方式的场景。