数据结构---------二叉树前序遍历中序遍历后序遍历

news/2024/12/23 21:09:04 标签: 数据结构

以下是用C语言实现二叉树的前序遍历、中序遍历和后序遍历的代码示例,包括递归和非递归(借助栈实现)两种方式:

1. 二叉树节点结构体定义

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

// 二叉树节点结构体
typedef struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;

2. 前序遍历

(ps:图来自一位前辈,非常感谢无商用)
在这里插入图片描述

2.1 递归方式
// 前序遍历递归函数
void preorderTraversalRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    printf("%d ", root->val);
    preorderTraversalRecursive(root->left);
    preorderTraversalRecursive(root->right);
}

解释

  • 首先判断根节点是否为空(NULL),如果为空,说明已经遍历完或者二叉树本身就是空树,直接返回,不做任何操作。
  • 若根节点不为空,则先输出根节点的值(通过printf函数),这符合前序遍历“根节点、左子树、右子树”的顺序。
  • 接着递归调用preorderTraversalRecursive函数去遍历左子树,完成左子树的前序遍历。
  • 最后再递归调用该函数遍历右子树,完成整个二叉树的前序遍历。
2.2 非递归方式(借助栈实现)
// 前序遍历非递归函数(借助栈)
void preorderTraversalNonRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    struct TreeNode *stack[100];  // 简单起见,这里假设栈的最大容量为100,可以根据实际情况调整
    int top = -1;
    stack[++top] = root;
    while (top >= 0) {
        TreeNode* current = stack[top--];
        printf("%d ", current->val);
        if (current->right!= NULL) {
            stack[++top] = current->right;
        }
        if (current->left!= NULL) {
            stack[++top] = current->left;
        }
    }
}

解释

  • 同样先判断根节点是否为空,为空则直接返回。
  • 然后创建一个数组来模拟栈(这里简单地定义了固定大小为100的数组作为栈,实际应用中可根据二叉树规模动态分配内存),并初始化栈顶指针top为 -1,表示栈为空。
  • 将根节点入栈后,进入循环,只要栈不为空(即top >= 0):
    • 取出栈顶元素(current = stack[top--]),输出其值,这模拟了访问根节点的操作。
    • 按照前序遍历先右后左的顺序将子节点入栈(因为栈是后进先出的数据结构,先入栈右子节点,后入栈左子节点,这样出栈时就能先访问左子树),如果右子节点或左子节点不为空,就将它们依次入栈,以便后续继续遍历。

3. 中序遍历

在这里插入图片描述

3.1 递归方式
// 中序遍历递归函数
void inorderTraversalRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    inorderTraversalRecursive(root->left);
    printf("%d ", root->val);
    inorderTraversalRecursive(root->right);
}

解释

  • 先判断根节点是否为空,为空则返回。
  • 按照中序遍历“左子树、根节点、右子树”的顺序,首先递归调用inorderTraversalRecursive函数去遍历左子树,确保左子树的节点先被访问。
  • 当左子树遍历完后,输出根节点的值。
  • 最后再递归调用该函数遍历右子树,完成整个二叉树的中序遍历。
3.2 非递归方式(借助栈实现)
// 中序遍历非递归函数(借助栈)
void inorderTraversalNonRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    struct TreeNode *stack[100];  // 假设栈最大容量为100,可按需调整
    int top = -1;
    TreeNode* current = root;
    while (current!= NULL || top >= 0) {
        while (current!= NULL) {
            stack[++top] = current;
            current = current->left;
        }
        current = stack[top--];
        printf("%d ", current->val);
        current = current->right;
    }
}

解释

  • 还是先判断根节点是否为空,为空则返回。
  • 创建一个栈并初始化栈顶指针,同时用current指针指向根节点。
  • 进入循环,只要current不为空或者栈不为空:
    • 先通过内层循环将当前节点及其所有左子树节点依次入栈(不断将current指向其左子节点并入栈),直到current为空,这意味着找到了最左边的节点。
    • 然后取出栈顶元素(即最左边的节点),输出其值,这模拟了访问根节点的操作(在中序遍历中此时访问的是左子树遍历完后的根节点)。
    • 最后将current更新为该节点的右子节点,以便继续遍历右子树,重复上述过程,完成中序遍历。

4. 后序遍历

在这里插入图片描述

4.1 递归方式
// 后序遍历递归函数
void postorderTraversalRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    postorderTraversalRecursive(root->left);
    postorderTraversalRecursive(root->right);
    printf("%d ", root->val);
}

解释

  • 首先判断根节点是否为空,为空则返回,不做后续操作。
  • 按照后序遍历“左子树、右子树、根节点”的顺序,先递归调用postorderTraversalRecursive函数遍历左子树。
  • 接着递归调用该函数遍历右子树。
  • 最后输出根节点的值,完成整个二叉树的后序遍历。
4.2 非递归方式(借助栈实现)
// 后序遍历非递归函数(借助栈)
void postorderTraversalNonRecursive(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    struct TreeNode *stack1[100];  // 假设栈1最大容量为100,可按需调整
    struct TreeNode *stack2[100];  // 假设栈2最大容量为100,可按需调整
    int top1 = -1, top2 = -1;
    stack1[++top1] = root;
    while (top1 >= 0) {
        TreeNode* current = stack1[top1--];
        stack2[++top2] = current;
        if (current->left!= NULL) {
            stack1[++top1] = current->left;
        }
        if (current->right!= NULL) {
            stack1[++top1] = current->right;
        }
    }
    while (top2 >= 0) {
        printf("%d ", stack2[top2--]->val);
    }
}

解释

  • 先判断根节点是否为空,为空则返回。
  • 创建两个栈stack1stack2(这里同样是简单地假设了固定大小为100的数组来模拟栈,实际可按需优化),以及对应的栈顶指针top1top2,并将根节点入stack1
  • 在第一个循环中:
    • stack1中取出栈顶元素,放入stack2中,这一步是为了后续能按后序遍历的顺序输出节点。
    • 然后按照后序遍历先左后右的顺序将其左子节点和右子节点(如果存在)依次入stack1,方便后续处理。
  • 第一个循环结束后,stack2中存储的节点顺序就是后序遍历的顺序了,通过第二个循环依次输出stack2中节点的值,完成二叉树的后序遍历。

你可以使用以下代码来测试这些遍历函数:

int main() {
    // 构建一个简单的二叉树示例
    TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
    root->val = 1;
    root->left = (TreeNode*)malloc(sizeof(TreeNode));
    root->left->val = 2;
    root->left->right = (TreeNode*)malloc(sizeof(TreeNode));
    root->left->right->val = 4;
    root->right = (TreeNode*)malloc(sizeof(TreeNode));
    root->right->val = 3;
    root->right->left = (TreeNode*)malloc(sizeof(TreeNode));
    root->right->left->val = 5;

    // 测试前序遍历
    printf("前序遍历(递归)结果:");
    preorderTraversalRecursive(root);
    printf("\n");
    printf("前序遍历(非递归)结果:");
    preorderTraversalNonRecursive(root);
    printf("\n");

    // 测试中序遍历
    printf("中序遍历(递归)结果:");
    inorderTraversalRecursive(root);
    printf("\n");
    printf("中序遍历(非递归)结果:");
    inorderTraversalNonRecursive(root);
    printf("\n");

    // 测试后序遍历
    printf("后序遍历(递归)结果:");
    postorderTraversalRecursive(root);
    printf("\n");
    printf("后序遍历(非递归)结果:");
    postorderTraversalNonRecursive(root);
    printf("\n");

    return 0;
}

在上述main函数中,构建了一个简单的二叉树示例,然后分别调用不同的遍历函数并输出结果,方便直观地看到不同遍历方式的效果。实际应用中,你可以根据实际的二叉树结构来进行相应的测试和使用这些遍历方法。

在这里插入图片描述


http://www.niftyadmin.cn/n/5797004.html

相关文章

Linux网络功能 - 服务和客户端程序CS架构和简单web服务示例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 概述准备工作扫描服务端有那些开放端口创建客户端-服务器设置启动服务器和客户端进程双向发送数据保持服务器进程处于活动状态设置最小…

【优选算法---分治】快速排序三路划分(颜色分类、快速排序、数组第K大的元素、数组中最小的K个元素)

一、颜色分类 题目链接: 75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; 题目介绍&#xff1a; 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地 对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序…

目标检测文献阅读-Faster R-CNN:通过区域建议网络实现实时目标检测(12.16-12.22)

目录 摘要 Abstract 1 引言 2 Fast R-CNN 2.1 RoI池化层 2.2 多任务损失 3 RPN 3.1 Anchors 3.2 损失函数 3.3 训练RPN 4 RPN和Fast R-CNN共享特征 总结 摘要 本周阅读的论文题目是《Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Netw…

MySQL 中的常见错误与排查

在 MySQL 数据库的日常运维中&#xff0c;管理员可能会遇到各种错误。无论是查询性能问题、连接异常、数据一致性问题&#xff0c;还是磁盘空间不足等&#xff0c;及时排查并解决这些问题是保证数据库稳定运行的关键。本文将列出 MySQL 中一些常见的错误及其排查方法。 一、连接…

centOS系统进程管理基础知识

进程的概念与属性 1.进程是系统中正在执行的代码片段&#xff0c;也可以称为一个程序。 2.操作系统通过分配进程编号&#xff08;PID&#xff09;来管理进程。 3.进程属性包括PID、PPID、UID、GID、状态、优先级、终端名和资源占用等。 PS命令与进程查看 1.PS命令用于查看进程…

设计模式 -- 单例模式

设计模式 -- 单例模式 单例模式C++ 实现饿汉式单例模式懒汉式单例模式使用静态局部变量实现懒汉式单例模式(推荐)使用call_once实现懒汉式单例模式(推荐)使用静态全局部变量和指针的方式实现懒汉式单例模式(不推荐)双重检测懒汉式单例模式单例模式 单例模式:确保在整个程…

xenomai环境下开源实时数控系统LinuxCNC EtherCAT编译安装

LinuxCNC是一款基于Linux操作系统的开源实时数控系统&#xff0c;可将普通计算机转变为高效的CNC&#xff08;计算机数字控制&#xff09;机器&#xff0c;本文记录xenomai下linuxcnc的构建简单记录&#xff0c;xenomai下构建无特别之处&#xff0c;主要参考链接https://www.li…

【Spring】Spring框架之-AOP

目录 1. AOP的引入 2. AOP相关的概念 2.1 AOP概述 2.2 AOP的优势 2.3. AOP的底层原理--目前先不具体阐述&#xff0c;后面讲 3. Spring的AOP技术-配置文件方式 3.1 AOP相关的术语 3.2 基本准备工作 3.3 AOP配置文件方式的入门 3.4 切入点的表达式 3.5 AOP的通知类型 …