【数据结构】二叉树的前中后序遍历(C语言)

简介: 【数据结构】二叉树的前中后序遍历(C语言)

什么是二叉树

[二叉树] 顾名思义就是有两个分支节点的树,不仅如此,除了叶子外的所有节点都具有两个分支节点;

由于结构像一棵倒立的树,顾名思义为二叉树

如下图所示,该图即为一棵野生的二叉树

既然二叉树为树,固然有着和树一样的部分(叶子、根、分支…)

这些也成为了树相关的概念;


树相关的概念

  • 叶子节点或者终端节点

叶子节点或终端节点,顾名思义就是最底端的节点,该节点不存在分支,故被称为叶子;


  • 节点的度

节点的度即为一个节点含有的子树成为该节点的度,如图所示,节点C的度为3;


  • 双亲结点(父节点)

若是一个节点存在子节点则该节点成为该子节点的父节点;如上图所示,A节点既是B的父节点,也是C的父节点;


  • 兄弟节点

具有相同父节点的节点成称为兄弟节点,“双亲结点”中所提到的“A结点既是B的父节点,也是C的父结点”处中的BC节点即为兄弟节点。


  • 树的度

树的度即为一棵树中,最大的节点的度,如“结点的度”中的C节点,该节点为整棵树中度最大的结点为3,故该树的度为3;


  • 树的高度或深度

树中结点的最大层次即为树的高度,如上图所示,树的高度为3;


  • 堂兄弟节点

双亲在同一层的节点互为堂兄弟,如上图所示,E节点和F节点护卫堂兄弟节点;


  • 节点的祖先

从根到节点所经分支上的所有节点,如上图所示,A节点为所有节点的祖先;


  • 子孙

以某节点为根的子树中任意一个节点都称为该节点的子孙

如上图所示,除A以外的所有其他节点都为A节点的子孙;


  • 森林

由m(m>0)棵互不相交的树的集合称为森林;


树的表示形式

树的结构与线性表相比就会更加复杂,既要保存值域,同时也要保存树中节点与节点之间的关系;在树中有许多的表示方法(双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法等等),在此就不做过多赘述;

既然树的结构如此复杂,那对于真正实际中,树有什么应用呢?大家可以将计算机中的文件夹进行比较,从一个文件夹可以分支出很多个文件夹,文件夹内还可以继续存放文件夹,如此;

在Linux操作系统就应用了文件目录树,目录树的起点是根目录,Linux文件系统中的每一文件在此目录树中的文件名都是独一无二的,因为其包含从根目录开始的完整路径;

同时在很多算法中,都运用到了树;


特殊的二叉树

二叉树在树中也算是一个比较特殊的树种,但在二叉树中也存在着特殊的二叉树,即为完全二叉树与满二叉树

  • 满二叉树
    一个二叉树,如果每一层的节点树都达到最大值,则这个二叉树就是满二叉树;也就是说,如果一个二叉树的层数为k,且节点总数是2^k-1,则它就是满二叉树;
  • 完全二叉树
    完全二叉树是一种效率很高的树,是由满二叉树引申出来的。对于深度为k的,有n个节点的二叉树,当且仅当其每个节点都与深度k的满二叉树中编号从1至n的节点一一对应时称为完全二叉树,同时满二叉树也是一种特殊的完全二叉树。

如何创造出一棵二叉树

在不使用任何算法的前提下若是想得到一棵二叉树可以使用比较简单粗暴的方式:

将各个节点创建并将每个节点连接在一起(该方法适用于任何一种树);

根据树的结构我们可以得知,树既要保存值域,同时也要保存节点之间的关系,又因为为二叉树(每个节点必有两个分支)

假设需要创建一个如图所示的二叉树

在此处可以定义一个结构体,并在结构体内存放结构体指针用来存放左右两个子树,同时创建一个成员变量用来存放所需要存储的值域

typedef char BTDataType;
typedef struct BinaryNode
{
  struct BinaryNode*left;
  struct BinaryNode*right;
  BTDataType a;
}BTNode;

根据图所示可知,A节点的子节点为B、C;

B节点的子节点为D、E;

C节点的子节点为F、G;

int main()
  {
    BTNode q1;
    BTNode q2;
    BTNode q3;
    BTNode q4;
    BTNode q5;
    BTNode q6;
    BTNode q7;
    q1.a = 'A';
    q2.a = 'B';
    q3.a = 'C';
    q4.a = 'D';
    q5.a = 'E';
    q6.a = 'F';
    q7.a = 'G';
    q1.left = &q2;
    q1.right = &q3;
    q2.left = &q4;
    q2.right = &q5;
    q3.left = &q6;
    q3.right = &q7;
    q4.left = q4.right = q5.left = q5.right =q6.left = q6.right = q7.left = q7.right =NULL;
    return 0;
  }

二叉树的遍历

二叉树的遍历分为先序遍历,中序遍历,后序遍历以及层序遍历

为什么会叫先中后,顾名思义,这里的先中后为遍历过程中根节点的访问顺序,一棵树的任意子树都可以看成根节点和子树;

至于层序遍历,即为一层一层的进行访问;

首先设存在一棵二叉树

先序遍历(前序遍历)

先序遍历的遍历方式为:根、左子树、右子树

按照该树可以得出,该树的前序遍历为:A,B,D,E,C,F,G

根据根、左子树、右子树的顺序可知,最先访问的必定是A节点,当A节点访问完即访问左子树,而左子树的根节点为B,B访问结束访问B的左子树,为D,D没有左右子树(为空)故返回访问B的右子树,E与D同理,而后B访问结束放回根节点A并访问A的右树,同理得出该树的前序遍历为 A,B,D,E,C,F,G

既然看图可以得出树的前序遍历,那在代码中如何表示出二叉树的前序遍历,该处只需要使用递归的方式,按照根、左、右的方式即可;

void BinaryTreePreOrder(BTNode*root)
{
  if(root==NULL){//当root为空时则表示该处无节点,即无存储有效数据,若是访问则会造成对空指针的非法解引用
    printf("NULL");
    return ;
  }
  printf("%c ",root->a);//若是不为空则打印出该节点内所存储的有效数据
  BinaryTreePreOrder(root->left);//访问该节点的左子树
  BinaryTreePreOrder(root->right);//访问该节点的右子树
}

如图所示


中序遍历

中序遍历的遍历顺序为:左子树、根、右子树

按照该树可得出中序遍历的结果为:D,B,E,A,F,C,G;

若是用代码方式表示即为:

void BinaryTreeInOrder(BTNode*root)
{
  if(root==NULL){//当root为空时则表示该处无节点,即无存储有效数据,若是访问则会造成对空指针的非法解引用
    printf("NULL");
    return;
  }
  printf("%c ",root->a);//若是不为空则打印出该节点内所存储的有效数据
  BinaryTreeInOrder(root->left);//访问该节点的左子树
  BinaryTreeInOrder(root->right);//访问该节点的右子树
}

后序遍历

中序遍历的遍历顺序为:左子树、右子树、根

按照该树可得出中序遍历的结果为:D,E,B,F,G,C,A;

若是用代码方式表示即为:

void BinaryTreePostOrder(BTNode*root)
{
  if(root==NULL){//当root为空时则表示该处无节点,即无存储有效数据,若是访问则会造成对空指针的非法解引用
    printf("NULL");
    return;
  }
  BinaryTreePostOrder(root->left);//访问该节点的左子树
  BinaryTreePostOrder(root->right);//访问该节点的右子树
  printf("%c ",root->a);//若是不为空则打印出该节点内所存储的有效数据
}

总结

二叉树除了前中后序遍历以外还有一种遍历方式叫作层序遍历,可以使用队列的FIFO特性从而完成该遍历的实现;

在利用递归实现解决二叉树相关问题的过程中,可以根据实际情况选择相应的遍历方式从而以效率较高的方式解决问题;

所有的二叉树问题都可以将其分为两个子问题进行解决;

相关文章
|
8天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
8天前
|
C语言
数据结构基础详解(C语言):图的基本概念_无向图_有向图_子图_生成树_生成森林_完全图
本文介绍了图的基本概念,包括图的定义、无向图与有向图、简单图与多重图等,并解释了顶点度、路径、连通性等相关术语。此外还讨论了子图、生成树、带权图及几种特殊形态的图,如完全图和树等。通过这些概念,读者可以更好地理解图论的基础知识。
|
10天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
10天前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
10天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
10天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
7天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
10天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
10天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
16天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。