1、先序遍历(根 -> 左 -> 右)
Step1: 初始化栈:将根节点压入栈中。
Step2: 循环处理栈(直到栈为空)
- 弹出栈顶节点并访问(如打印值)。
- 先压右孩子(如果存在):将当前节点的右孩子压入栈。
- 再压左孩子(如果存在):将当前节点的左孩子压入栈。
原理:先压右孩子,再压左孩子,保证弹出时先处理左孩子,符合先序遍历的顺序。
void preOrder(TreeNode* root)
{
if (root == nullptr) return;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty())
{
TreeNode* cur = stk.top();
cout << cur->val << " ";
stk.pop();
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
}
}
2、后序遍历(左 -> 右 -> 根)
方法一:使用两个辅助栈
Step1: 初始化双栈
- 主栈(stk):处理节点遍历顺序,初始压入根节点。
- 打印栈(
printStk
):暂存最终要输出的节点。
Step2: 循环处理主栈
- 弹出主栈顶节点,并压入打印栈(此时打印栈顺序为:根→右→左)。
- 先压左孩子(如果存在):左孩子先压入主栈,后续会被后处理。
- 再压右孩子(如果存在):右孩子后压入主栈,成为下一次循环的栈顶。
- 循环终止条件:主栈为空,所有节点已处理完毕。
Step3: 逆序输出打印栈
- 依次弹出打印栈中的节点并访问,得到 左→右→根 的后序结果。
原理:主栈负责生成根右左的逆序,打印栈负责最终顺序翻转。
void postOrder1(TreeNode* root)
{
if (root == nullptr) return;
stack<TreeNode*> stk;
stack<TreeNode*> printStk;
stk.push(root);
while (!stk.empty())
{
TreeNode* cur = stk.top();
printStk.push(cur);
stk.pop();
if (cur->left) stk.push(cur->left);
if (cur->right) stk.push(cur->right);
}
while (!printStk.empty())
{
cout << printStk.top()->val << " ";
printStk.pop();
}
}
方法二:使用一个辅助栈
Step1: 初始化栈和指针
- 栈中压入根节点。
cur
:指向当前处理的节点(初始为根节点)。pre
:记录上一个已访问的节点(初始为根节点,仅用于逻辑判断)。
Step2: 循环处理栈顶节点
- 循环条件:栈不为空。
- 步骤分解:
a. 左子树未处理:若当前节点的左子存在且未被访问(cur->left != pre
),且右子也未被访问(cur->right != pre
),则压入左子。
b. 右子树未处理:若左子树已处理,但右子存在且未被访问(cur->right != pre
),则压入右子。
c. 访问根节点:若左右子树均已处理(或不存在),则弹出栈顶节点并访问,更新pre
为当前节点。
原理:
-
pre
指针的作用:标记最近访问的节点,避免重复处理子树。- 当
pre == cur.left
时,说明左子树已处理完。 - 当
pre == cur.right
时,说明右子树已处理完。
- 当
void postOrder2(TreeNode* root)
{
if (root == nullptr) return;
stack<TreeNode*> stk;
stk.push(root);
TreeNode* pre = root; // 记录上一个打印的节点,最开始还没有打印时就放在root的位置,不干扰将左边界全部入栈的过程
TreeNode* cur = root; // 处理的当前节点
while (!stk.empty())
{
cur = stk.top();
if (cur->left != nullptr && cur->left != pre && cur->right != pre) // 如果左子树还没有处理完,就处理左子树。(什么时候处理完)
{
stk.push(cur->left);
}
else if (cur->right != nullptr && cur->right != pre) // 如果右子树还没有处理完,就处理右子树
{
stk.push(cur->right);
}
else
{
cout << stk.top()->val << " ";
pre = stk.top(); // 记录上次打印的节点
stk.pop();
}
}
}
3、中序遍历(左 -> 根 -> 右)
Step1: 初始化栈和当前节点:当前节点 为根节点 root,
准备一个空栈 stk
。
Step2: 循环处理节点(直到 root
为空且栈为空)
- 左子树处理到底:
- 若
root
不为空,将root
压入栈,并更新root
为左子节点,直到左子节点为空。
- 若
- 回溯访问根节点:
- 当
root
为空时,弹出栈顶节点(即当前子树的根节点),访问该节点。
- 当
- 转向右子树:
- 将
root
更新为弹出节点的右子节点,继续处理右子树的左边界。
- 将
void inOrder(TreeNode* root)
{
if (root == nullptr) return;
stack<TreeNode*> stk;
while (root != nullptr || !stk.empty())
{
if (root)
{
stk.push(root);
root = root->left;
}
else
{
root = stk.top();
stk.pop();
cout << root->val << " ";
root = root->right;
}
}
}
4、测试代码
以如下的二叉树为例,测试上面的函数:
int main()
{
TreeNode* node1 = new TreeNode(1);
TreeNode* node2 = new TreeNode(2);
TreeNode* node3 = new TreeNode(3);
TreeNode* root = new TreeNode(4);
TreeNode* node5 = new TreeNode(5);
TreeNode* node6 = new TreeNode(6);
TreeNode* node7 = new TreeNode(7);
root->left = node2;
root->right = node6;
node2->left = node1;
node2->right = node3;
node6->left = node5;
node6->right = node7;
preOrder(root);
cout << endl;
postOrder1(root);
cout << endl;
postOrder2(root);
cout << endl;
inOrder(root);
}
运行结果:
4 2 1 3 6 5 7
1 3 2 5 7 6 4
1 3 2 5 7 6 4
1 2 3 4 5 6 7