数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解

简介: 本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。

本文逻辑:
本文由二叉树的遍历起手,讲解了二叉树的三种遍历方式,以及如何构造一颗二叉树,并在此基础上,扩展了更好的二叉树-线索二叉树。树和森林的存储结构讲解中,重点就是将树与森林转换为二叉树,这样二叉树的手段就能使用到树与森林当中。最后,讲解了二叉树与森林的遍历。

1.二叉树的遍历

什么是遍历
遍历:按照某种次序把所有的结点都访问一遍
什么是层次遍历:基于树的层次特性确定的次序规则(从上到下,从左到右的遍历)

二叉树的递归特性:
:one: 要么是个空二叉树
:two: 要么就是由根节点+左子树+右子树 组成的二叉树

1.1 二叉树的前,中,后序遍历

二叉树有三种遍历的情形
:one:先序遍历:根左右
:two:中序遍历:左根右
:three: 后序遍历:左右根

image.png

二叉树的遍历(手算)

给你一个二叉树,写出他的先序遍历,中序遍历,后序遍历,属于简单题
给出一个手算技巧,留出空,根据遍历规则,补全

1.2 二叉树的层次遍历

  • 算法思想
    :one: 初始化一个辅助队列
    :two: 根结点入队
    :three: 若队列为空,则头结点出队。将其左孩子,右孩子插入队尾(如果有的话
    :four: 重复:three:直至队列为空

1.3构造二叉树

直接说结论:
想要构造一个二叉树,必须知道的两种遍历序列,其中还必须有中序遍历。
也就是中序遍历+(前序遍历or后序遍历or层次遍历)

线索二叉树引入:

在二叉树的基础上,我们能否从一个指定结点开始中序遍历?
如何找到指定结点p在中序遍历序列中的前驱
如何找到p的中序后继
解决思路:
两个指针+目标结点p
从根节点出发,重新进行一次中序遍历,指针q记录当前访问的结点,指针pre记录上一个被访问的结点
当q==p时,pre为前驱
当pre==p时,q为后继。
通过这种方法解决还是太麻烦了,所以引入线索二叉树

2.线索二叉树

2.1 以中序线索二叉树为例,讲解线索二叉树

n个结点的二叉树,有n+1个空链域,用来构成线索,记录前驱和后继

  • 前驱线索(由左孩子指针充当)
  • 后继线索(由右孩子指针充当)
    image.png

三种线索二叉树的对比
image.png

2.2 二叉树的线索化

为了区分指针是指向了孩子还是指向了前驱/后继
我们还需要两个标志 一个是ltag,一个是rtag,记录当前结点是否记录了线索

  • ltag=0,lchild指向左孩子
  • ltag=1,lchild指向前驱
  • rtag=0,lchild指向右孩子
  • rtag=1,lchild指向后继

一棵二叉树的中序线索化:
1.首先取得二叉树的中序序列,
假设是A~4~,A~2~,A~5~,A~1~,A~6~,A~3~
2.A~4~的前驱指针指向NULL,A~3~的后继指针指向NULL
3.遍历一遍树,如果左指针为空就指向前驱,如果右指针为空就指向后继。

2.3 线索二叉树中找前驱后继

2.3.1 中序线索二叉树寻找前驱后继(重点)

中序线索二叉树找中序后继

明确p点肯定有右孩子,没有右孩子的话,中序后继直接为NULL了
根据左根右,他的后继是右,也就是右子树,右孩子如果还有孩子,那继续左根右,即首先访问最左的点
综上所述,中序后继=p的右子树中最左下结点。

中序线索二叉树找中序前驱
有了之前的分析过程,很好推出,中序前驱=p的左子树中最右下结点

2.3.2 先序线索二叉树寻找前驱后继(了解)

先序线索二叉树找先序前驱
无法实现,除非土办法遍历或者,使用三叉链表(能找到p结点的父结点)
下面假设可以找到p的父节点.
image.png

先序线索二叉树找先序后继
先序后继就是最左的节点

2.3.3 后序线索二叉树寻找前驱后继(了解)

后序线索二叉树寻找后序前驱
p肯定有左孩子,若p有右孩子,则后序前驱是他的右孩子,没有就是左孩子

后序线索二叉树寻找后序后继
无法实现,除非土办法遍历或者,使用三叉链表(能找到p结点的父结点)
下面假设可以找到p的父节点.

image.png

3.树的存储结构

  • 双亲表示法(顺序存储)
  • 孩子表示法(顺序+链式存储)
  • :star:孩子兄弟表示法(链式存储)

3.1 双亲表示法(顺序存储)

核心:每个结点中保存指向双亲的指针
image.png

增删改查思路:
增加一个孩子,直接在顺序的数组中增加即可
删除一个孩子,会让某一块为空,可以让最后一个存储的孩子,覆盖上去,提高效率
查找的话:想要找到某个结点的孩子,比较麻烦,要全部遍历一遍树

优缺点:
优点:查指定结点的双亲很方便。
缺点:查指定结点的孩子只能从头遍历。

3.2 孩子表示法(顺序+链式存储)

核心:结点顺序存储,每一个结点的孩子,用链式存储
在这里插入图片描述

3.3 孩子兄弟表示法(链式存储)->重点

核心目的:将逻辑结构树能够转换为存储结构是二叉树的形式储存,这样我们就可以用处理二叉树的手段,处理它。

核心思路:左指针指向第一个孩子(最左边的孩子),右指针指向他的右兄弟

我们可以 轻松的将一个树改写成这种孩子兄弟表示法的形式
在这里插入图片描述

3.4 森林和二叉树的转换

在3.3的基础上,我们不难理解森林和二叉树的转换,就是按照孩子兄弟表示法

4.树的遍历

  • 树的先根遍历

4.1 树的先根遍历(先根,先根,就是先遍历根,再遍历子树)

核心:先根遍历。若树非空,先访问根结点,再依次对每棵子树进行先根遍历。

与二叉树遍历对比:==树的先根遍历序列与二叉树的先序遍历相同==

4.2 树的后根遍历

核心:后根遍历。若树非空,先依次对每棵子树进行后根遍历,最后访问根结点
与二叉树遍历对比:==树的后根遍历序列与二叉树的中序遍历相同==

4.3 树的层次遍历

跟二叉树的层次遍历一样,用队列实现

5.森林的遍历

森林是m棵互不相交的树的集合。每棵树去掉根节点,其各个子树又组成森林。

5.1 先序遍历森林

若森林为非空,则按如下规则进行遍历;
访问森林中第一棵树的根结点。
先序遍历第一棵树中根结点的子树森林
先序遍历除去第一棵之后剩余的树构成的森林。

与==树的遍历==对比:==效果等同于依次对各个树进行先根遍历==

5.2 中序遍历森林

若森林为非空,则按如下规则进行遍历
中序遍历森林中第一棵树的根结点的子树森林
访问第一棵树的根结点。
中序遍历除去第一棵树之后剩余的树构成的森林。

与==树的遍历==对比:==效果等同于依次对各个树进行后根遍历==

相关文章
|
11月前
|
存储 C语言
c语言——二叉树
二叉树的概念在这里就不进行过多的赘述,那么主要说一下我认为重要的部分,第一点就是二叉树里面部分概念的理解:就比如说,你对于如何构建二叉树,掌握的十分深刻,但刷题的时候对于一些题目所给的概念不清楚,导致看不明白题目,这课不好,二叉树的概念如下图所示,其实都很简单,主要是当给他的名字时,你明不明白。还有对于满二叉树与完全二叉树。
304 0
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
534 10
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
405 10
|
存储 算法 C++
【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】
本任务要求编写程序实现图的邻接矩阵和邻接表的存储。需掌握带权有向图、图的邻接矩阵及邻接表的概念。邻接矩阵用于表示顶点间的连接关系,邻接表则通过链表结构存储图信息。测试输入为图的顶点数、边数及邻接矩阵,预期输出为Prim算法求解结果。通关代码提供了完整的C++实现,包括输入、构建和打印邻接矩阵与邻接表的功能。
699 10
|
存储 编译器 C语言
【C语言程序设计——选择结构程序设计】求一元二次方程的根(头歌实践教学平台习题)【合集】
本任务要求根据求根公式计算并输出一元二次方程的两个实根,精确到小数点后两位。若方程无实根,则输出提示信息。主要内容包括: - **任务描述**:使用求根公式计算一元二次方程的实根。 - **相关知识**:掌握 `sqrt()` 函数的基本使用方法,判断方程是否有实根。 - **编程要求**:根据输入的系数,计算并输出方程的根或提示无实根。 - **测试说明**:提供两组测试数据及预期输出,确保代码正确性。 - **通关代码**:包含完整的 C 语言代码示例,实现上述功能。 通过本任务,你将学会如何处理一元二次方程的求解问题,并熟悉 `sqrt()` 函数的使用。
340 5
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
本任务要求从键盘输入三个数,并按从小到大的顺序排序后输出。主要内容包括: - **任务描述**:实现三个数的排序并输出。 - **编程要求**:根据提示在编辑器中补充代码。 - **相关知识**: - 选择结构(if、if-else、switch) - 主要语句类型(条件语句) - 比较操作与交换操作 - **测试说明**:提供两组测试数据及预期输出。 - **通关代码**:完整代码示例。 - **测试结果**:展示测试通过的结果。 通过本任务,你将掌握基本的选择结构和排序算法的应用。祝你成功!
298 4
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】求阶跃函数的值(头歌实践教学平台习题)【合集】
本任务要求输入x的值,计算并输出特定阶跃函数的结果。主要内容包括: 1. **选择结构基本概念**:介绍if、if-else、switch语句。 2. **主要语句类型**:详细解释if、if-else、switch语句的使用方法。 3. **跃迁函数中变量的取值范围**:说明如何根据条件判断变量范围。 4. **计算阶跃函数的值**:通过示例展示如何根据给定条件计算函数值。 编程要求:在右侧编辑器Begin-End之间补充代码,实现阶跃函数的计算和输出。测试说明提供了多个输入及其预期输出,确保代码正确性。最后提供通关代码和测试结果,帮助理解整个过程。
349 0
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
408 59
|
11月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
235 0
栈区的非法访问导致的死循环(x64)