目录
题目
什么是前序遍历
前序遍历(Preorder Traversal)是一种遍历树形结构的方法,特别是在二叉树中常用。它的遍历顺序为:
- 先访问根节点
- 然后递归地前序遍历左子树
- 最后递归地前序遍历右子树
这种遍历方式也称为"深度优先遍历"(DFS)的一种形式。
示例
对于以下二叉树:
A
/ \
B C
/ \ \
D E F
前序遍历的结果是:A → B → D → E → C → F
递归的写法
cpp
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
preorder(root, result);
return result;
}
void preorder(TreeNode* node, vector<int>& result) {
if (node == nullptr) {
return;
}
// 前序遍历顺序:根 -> 左 -> 右
result.push_back(node->val); // 访问根节点
preorder(node->left, result); // 递归访问左子树
preorder(node->right, result); // 递归访问右子树
}
非递归的写法
思路
初始化:
- 创建一个空的结果数组 result 用于存储遍历结果
- 创建一个栈 stack 用于存储待访问的节点
- 检查根节点是否为空,如果为空直接返回空结果
算法流程:
- 将根节点压入栈中
- 当栈不为空时,重复以下步骤:
弹出栈顶节点 current
将 current 的值加入结果数组(访问当前节点)
关键点:先将右子节点压栈,再将左子节点压栈
- 这是因为栈是后进先出(LIFO)的数据结构
- 我们希望左子节点先于右子节点被处理,所以右子节点要先入栈
继续循环,直到栈为空
为什么这样工作:
- 每次我们弹出一个节点,立即访问它(符合前序的"先访问根"原则)
- 然后将其子节点以"右-左"顺序压栈,这样出栈顺序就是"左-右"
- 这保证了对于每个子树,我们都是按照"根-左-右"的顺序访问
复杂度分析:
- 时间复杂度:O(n),其中 n 是树中的节点数,每个节点被访问一次
- 空间复杂度:O(h),其中 h 是树的高度,最坏情况下为 O(n)(树完全不平衡)
实现
cpp
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
if (root == nullptr) {
return result;
}
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
// 弹出栈顶节点并访问
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
// 注意:先压入右子节点,再压入左子节点
// 这样出栈时才会先处理左子节点(栈是后进先出)
if (node->right) {
st.push(node->right);
}
if (node->left) {
st.push(node->left);
}
}
return result;
}