后序遍历的递归算法核心思想是:按照“左子树 → 右子树 → 根节点”的顺序访问二叉树中的每个节点

后序遍历的递归算法核心思想是:按照“左子树 → 右子树 → 根节点”的顺序访问二叉树中的每个节点。以下是完整的C语言实现示例:

#include <stdio.h>
#include <stdlib.h>

// 二叉树节点定义
typedef struct TreeNode {
    int data;
    struct TreeNode* lchild;
    struct TreeNode* rchild;
} TreeNode;

// 后序遍历递归实现
void PostOrder(TreeNode* root) {
    if (root != NULL) {
        PostOrder(root->lchild);  // 遍历左子树
        PostOrder(root->rchild);  // 遍历右子树
        printf("%d ", root->data); // 访问根节点
    }
}

关于三种遍历的路线逻辑,可以形象地理解为从根节点出发,沿二叉树外缘逆时针走一圈,每个节点会被经过三次:

  • 第一次经过时访问 → 先序遍历(根左右)
  • 第二次经过时访问 → 中序遍历(左根右)
  • 第三次经过时访问 → 后序遍历(左右根)

时间复杂度分析:
由于每个节点仅被访问一次,无论哪种遍历方式,总时间复杂度均为 O(n)

空间复杂度分析:
主要消耗在系统调用栈上,取决于递归深度,即树的高度 h:

  • 平均情况(平衡树):O(log n)
  • 最坏情况(单枝树,退化为链表):O(n)

非递归遍历的基本思路是使用显式栈模拟函数调用栈的行为。以中序遍历为例,其非递归实现如下:

#include <stdbool.h>

void InOrderNonRecursive(TreeNode* root) {
    TreeNode* stack[100];
    int top = -1;
    TreeNode* p = root;

    while (p != NULL || top != -1) {
        if (p != NULL) {
            stack[++top] = p;
            p = p->lchild;  // 一直向左走
        } else {
            p = stack[top--];           // 出栈
            printf("%d ", p->data);     // 访问根
            p = p->rchild;              // 转向右子树
        }
    }
}

对于后序遍历的非递归实现,较为复杂,通常需要记录前驱节点或使用标志位来判断是否已遍历完左右子树。
实现二叉树的后序遍历非递归算法的关键在于:在不使用递归的情况下,正确判断何时访问根节点。由于后序遍历是“左→右→根”,我们必须确保在访问根节点前,其左右子树都已处理完毕。

常用方法是使用一个栈来保存待处理的节点,并借助一个指针记录上一次访问的节点(prev),从而判断当前节点是否可以被访问。

✅ 后序遍历非递归算法(单栈 + 标记法)

#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode {
    int data;
    struct TreeNode* lchild;
    struct TreeNode* rchild;
} TreeNode;

void PostOrderNonRecursive(TreeNode* root) {
    if (root == NULL) return;

    TreeNode* stack[100];  // 模拟栈
    int top = -1;
    TreeNode* prev = NULL;  // 记录上一次访问的节点
    TreeNode* curr = root;

    while (curr != NULL || top != -1) {
        // 一直走到最左边
        if (curr != NULL) {
            stack[++top] = curr;
            curr = curr->lchild;
        } else {
            // 查看栈顶元素(不弹出)
            curr = stack[top];

            // 如果右子树为空或已被访问过,则可以访问当前节点
            if (curr->rchild == NULL || curr->rchild == prev) {
                printf("%d ", curr->data);
                top--;                   // 弹出栈
                prev = curr;             // 更新已访问节点
                curr = NULL;             // 当前节点置空,避免重复入栈
            } else {
                // 否则转向右子树
                curr = curr->rchild;
            }
        }
    }
}

🔍 算法逻辑说明:

  • 入栈顺序:从根开始一路向左,将路径上的所有节点入栈。
  • 判断访问时机
    • 若当前节点无右子树 → 可直接访问;
    • 或右子树已经被访问过(prev == curr->rchild)→ 表示左右子树均已处理,可访问根;
  • 否则:转向右子树继续处理。

🧪 示例:

对于如下二叉树:

    1
   / \
  2   3
 / \
4   5

后序遍历结果应为:4 5 2 3 1

该算法能正确输出此序列。


⏱️ 复杂度分析:

  • 时间复杂度:O(n),每个节点被压栈和弹栈一次。
  • 空间复杂度:O(h),h 为树的高度,最坏 O(n),平均 O(log n)。
  • 在这里插入图片描述
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值