数据结构学习笔记(5.树与二叉树 6.图)

本文详细介绍了数据结构中的树与二叉树,包括它们的定义、性质、存储结构以及遍历方法。二叉树的特殊类型如满二叉树和完全二叉树,以及线索二叉树的概念和实现。接着讲解了图的基本概念、存储方法(邻接矩阵、邻接表)、遍历(BFS和DFS)和最小生成树、最短路径算法(BFS、Dijkstra、Floyd)。此外,还讨论了有向无环图(DAG)的拓扑排序和关键路径分析。内容深入浅出,是数据结构学习的重要参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第五章 树与二叉树

在这里插入图片描述
树的基本概念

  • 空树,非空树
  • 非空树特性

在这里插入图片描述
树的基本概念

  • 数学描述
  • 子树,子树之间互不相交
  • 树是一种递归定义的结构,任何一个树都可以看做是由根节点和若干个不相交的子树所组成的

在这里插入图片描述
树形逻辑结构的应用
在这里插入图片描述
结点之间的关系描述

  • 祖先结点:从一个节点出发一直往上走,直到根节点,所遇到的所有节点都是祖先结点
  • 子孙结点:从一个节点出发,下面所有长出的节点都是他的子孙结点
  • 双亲结点:一个结点的直接前驱,就是双亲结点,也叫父结点
  • 孩子结点:一个结点的直接后继,叫孩子结点
  • 兄弟结点:几个结点来自同一个父结点,他们之间就是兄弟结点
  • 堂兄弟结点:几个结点父结点不同,但是爷爷结点相同,之间就是堂兄弟节点;堂兄弟结点都在同一层

在这里插入图片描述
结点、树的属性描述

  • 我们默认结点的层次从1开始,也就是父结点为第一层
  • 有些地方叫法从0层开始,考试注意审题
    在这里插入图片描述
    有序树VS无序树
  • 取决于用树来做什么,是否需要结点的子树从左到右满足次序

在这里插入图片描述
树vs森林

  • 树和森林的转换也是考点
  • 树可以是空树,森林也可以为空森林

在这里插入图片描述
知识点小结
在这里插入图片描述

树的常考性质

考点1:结点数=总度数+1
在这里插入图片描述
考点2:度为m的树、m叉树的区别
在这里插入图片描述
考点2:度为m的树第i层,至多有m的i-1次方个结点
在这里插入图片描述
考点4:高度为h的m叉树,至多有几个结点,等比数列求和
在这里插入图片描述
考点5:
在这里插入图片描述
考点6:有n个结点的m叉树,最小高度为?让树尽可能的宽,参考前面的等比数列求和公式
在这里插入图片描述
知识点小结
在这里插入图片描述

二叉树–定义、基本术语

高频考点
在这里插入图片描述
二叉树的基本概念
在这里插入图片描述
二叉树的五种状态
在这里插入图片描述
考点:几个特殊的二叉树–形态上

  • 满二叉树,完全二叉树;满二叉树相当于特殊的完全二叉树
  • 完全二叉树如果不满的话,缺少的一定是最后面的几个叶子节点,前面的一定是按顺序排下来的,不能有跳跃
  • 如果完全二叉树的某个结点只有一个孩子,那么一定是左孩子

在这里插入图片描述
在这里插入图片描述
几个特殊的二叉树–功能上
在这里插入图片描述
几个特殊的二叉树–平衡二叉树

  • 平衡二叉树能有更高的搜索效率
  • 因为平衡二叉树生长的时候尽可能长的宽,搜索的时候才能尽可能减少深度,减少对比的次数

在这里插入图片描述
知识点小结
在这里插入图片描述

二叉树–常考性质

常见考点1:

  • 叶子结点比度为2的结点数多一个

在这里插入图片描述
常见考点2
在这里插入图片描述
常见考点3

  • 前面证明过

在这里插入图片描述
完全二叉树考点1

  • 要能自己推导,而不是死记硬背
    在这里插入图片描述
    在这里插入图片描述
    完全二叉树考点2
    在这里插入图片描述
    知识点小结
    在这里插入图片描述

二叉树–存储结构

二叉树的顺序存储
在这里插入图片描述
几个重要考点的基本操作

  • 结点编号的关系

在这里插入图片描述
如果不是完全二叉树,原有的编号顺序无法反应结点的逻辑关系
在这里插入图片描述

  • 可以仍然按照想象中的完全二叉树进行编号,不存在的结点去掉即可
  • 但是不能再通过结点总数来判断左右孩子是否存在,而是用isempty来判断
  • 显而易见,这样存储,会有大量的空间闲置浪费

在这里插入图片描述
最坏情况

  • 因此二叉树的顺序存储结构只适合存储完全二叉树
  • 实际中很少这么用

在这里插入图片描述
二叉树的链式存储–二叉链表

  • n个结点就会有2n个指针域
  • n个结点,每个结点的头上都有一个指针,除了根节点,因此共有n-1个指针有指向
  • 所以二叉树链表共有n+1个空链域

在这里插入图片描述
二叉链表代码实现
在这里插入图片描述
二叉链表存储

  • 找孩子非常容易,但是找父节点只能遍历寻找
  • 也可以个结点再添加一个指针,用来存放父节点,也成为三叉链表
  • 考试一般喜欢考二叉链表,然后遍历查找父节点

知识点小结

  • 顺序存储的情况,如果结点从0开始编号,如何调整
  • 链式存储的情况,需要注意空链域

在这里插入图片描述
在这里插入图片描述

二叉树–先序、中序、后序遍历

什么是遍历
在这里插入图片描述
二叉树的递归特性
在这里插入图片描述
二叉树遍历练习

  • 给定二叉树,确定前序中序后序遍历顺序是常见考点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先序遍历–代码实现
在这里插入图片描述
中序遍历–代码实现
在这里插入图片描述
后序遍历–代码实现
在这里插入图片描述
空间复杂度
在这里插入图片描述

  • 先脑补出空节点
  • 红色表示第一次访问该结点,绿色表示第二次经过该结点,紫色表示第三次经过该结点
  • 不难发现,每个节点都会被路过三次

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
应用
求树的深度
在这里插入图片描述
知识点小结
在这里插入图片描述

二叉树–层序遍历

层序遍历算法思想

  • 初始化辅助队列
  • 根结点入队
  • 队列不空则循环
    • 队列头结点出队
    • 访问出队的节点
    • 出队的节点如果有左右孩子,则入队
    • 重新判断是否循环

在这里插入图片描述
代码实现

  • 辅助队列使用链队列,因为不清楚需要使用多长的辅助队列
  • 辅助队列只需要保存结点的指针即可,无需保存结点数据,节省空间

在这里插入图片描述
知识点小结
在这里插入图片描述

由遍历序列构造二叉树

不同二叉树的遍历序列
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结论

  • 若只给出一颗二叉树的前序、中序、后序、层序中的一种排列,不能唯一确定一颗二叉树
  • 需要同时提供前序+中序、后序+中序、层序+中序两种排列,才能确定一颗二叉树
  • 必须包含中序遍历序列

在这里插入图片描述
如果给出 前序+中序 遍历序列

  • 前序遍历的第一个结点一定是跟结点
  • 在中序遍历中找到根结点的位置,根结点左侧的都是左子树的,根结点右侧的都是右子树的
  • 回到前序遍历序列,已知了根结点,左子树的结点,身下的就是右子树的结点
    在这里插入图片描述
    在这里插入图片描述
    BDC为左子树的结点,由前序遍历序列可知,D一定为左子树的根结点,以此类推,往下找
    在这里插入图片描述
    再看一个例子
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    如果给出 后序+中序 遍历序列
  • 原理类似,后序遍历序列中的最后一个点就是根结点
  • 在中序遍历序列中找到根结点的位置,左侧的为左子树的结点,右侧的为右子树的结点
  • 后面的结点推导类似

在这里插入图片描述
举个例子
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果给出 层序+中序 遍历序列

  • 层序遍历序列的第一个是根结点,然后是左子树根结点,右子树根结点

在这里插入图片描述
举个例子
在这里插入图片描述
AB是左右子树的根结点,由中序遍历序列可知,左右子树各自的下边的左右子树
在这里插入图片描述
在这里插入图片描述
再举个例子,左子树为空的情况
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
知识点小结

  • 重点是找到根结点
  • 通过根结点在中序遍历序列中的位置,分出左右子树的结点,找到左右子树的根结点
  • 再根据左右子树的跟结点,再在中序遍历序列中找到左右子树各自的左右子树
  • 以此类推
  • 中序遍历序列不可缺少,因为需要通过中序遍历序列区分左右子树
  • 前序、后序、层序的两两组合不能唯一确定二叉树

在这里插入图片描述
在这里插入图片描述

线索二叉树–概念

在这里插入图片描述
二叉树中序遍历

  • 二叉树中序遍历必须从根结点出发才能进行
  • 但是给定一个线性表,可以从任意结点向后遍历
  • 想要找到二叉树一个结点的前驱和后继,必须从头完整的遍历整个二叉树
  • 如果需要频繁查找任意结点的前驱、后继,普通的二叉树处理就会很麻烦
  • 这里我们说的前驱和后继,是中序遍历序列中的前驱和后继

在这里插入图片描述
中序线索二叉树

  • n个结点的二叉树,有n+1个空链域,可以用来记录前驱,后继信息
  • 将普通的二叉树进行中序线索化
  • 有了线索化,就意味着从一个点出发可以找到后继,也就可以实现从一个点向后遍历
  • 但是有些节点没有空链域,而是正常指向了左右孩子,如何找中序遍历的前驱和后继呢?后面小结讲
  • 有了线索,找遍历的前驱、后继更方便,遍历也更加方便

在这里插入图片描述
线索二叉树的存储结构

  • 如何区别结点中存储的是孩子还是线索?结点的结构体增加两个变量,存储左右的标记
  • 二叉树线索化之后也可叫做线索链表
  • 如果有空链域,左空链域存前驱,右空链域存后继

在这里插入图片描述
在这里插入图片描述
同理,有前序、后续的线索二叉树,原理都类似

  • 先确定遍历序列
  • 再找空链域进行线索化

在这里插入图片描述
在这里插入图片描述
后序线索二叉树
在这里插入图片描述
在这里插入图片描述
三种线索二叉树的对比

  • 区别在于确定各个结点的前驱、后继的时候,到底是按照中序遍历、前序遍历、还是后序遍历
  • 概念:中序前驱,指如果按照中序遍历的话,当前结点对应的前驱是什么;其他名字一次类推

在这里插入图片描述
知识点小结

  • 手算线索化

在这里插入图片描述

二叉树的线索化–代码实现

在这里插入图片描述
土办法找到中序前驱

  • 其实就是pre延迟一步,跟着q结点跑,直到p==q,则pre就是q上一步的结点,也就是p的前驱
  • 还是需要遍历一遍,比较low

在这里插入图片描述
既然要中序线索化,就要找前驱和后继,

  • 将上述土办法移至到二叉树线索的代码中
  • q指针用来中序遍历,pre慢一步跟着q跑
  • q结点添加前驱线索,pre添加后继线索
  • q和pre交替前进,刚好每个节点都会添加前驱和后继(如果有空链域的话)
  • 最后一步还要在外层函数中单独处理,让pre的后继线索为空,标记为1,因此需要设置pre为全局变量

在这里插入图片描述
中序线索化完整代码

  • 实际上就是在中序遍历访问各个结点的过程中,一边遍历一边处理这节点,进行线索化

在这里插入图片描述
在这里插入图片描述王道书中的代码

  • 书中的pre变成了局部变量而已,但是使用了引用参数,方便函数调用的时候能保存参数的变化

在这里插入图片描述
前序线索化

  • 原理都一样
  • 为了避免原地打转,访问结点时需要判断标记为,知道此时的左右孩子到底是子树,还是线索

在这里插入图片描述
在这里插入图片描述
王道书版本前序线索化

  • 最后一个结点pre的lchild必然是null
    在这里插入图片描述
    后续线索化
  • 原理一样
  • 后续线索化不会出现转圈问题

在这里插入图片描述
在这里插入图片描述
知识点小结
在这里插入图片描述

线索二叉树–如何找前驱、后继

在这里插入图片描述
中序线索二叉树找中序后继

  • p为根结点,如果p有后继指针,直接获取后继
  • 如果p没有后继指针,那么p的后继是右子树的第一个被中序遍历的结点
  • p为根子树,第一个被访问的结点就是最左下的结点(不一定是叶子结点)
  • 右子树第一个被访问的点,就是右子树最左下的点
  • 利用线索可以实现中序遍历,非递归算法,空间复杂度O(1)

在这里插入图片描述
中序线索二叉树找中序前驱

  • 中序序列中,找一个点的前驱就是这个点的左子树的最后一个被访问的点
  • 左子树最后一个被访问的结点就是这个左子树中最右下的结点
  • 如果左指针本来就是线索的话,直接返回线索即可
  • 找到最后的中序的遍历点,再不断地找前驱点,即可逆向遍历中序二叉树

在这里插入图片描述
前序线索二叉树找前序后继

  • 找p结点的前序后继要分情况讨论
  • p结点有左孩子和p结点没有左孩子的情况

在这里插入图片描述
前序线索二叉树找前序前驱

  • 如果p结点左指针是线索,那么直接返回前驱线索即可
  • 如果p结点左指针为左孩子,那么就找不到前驱了,因为前序遍历是 根 左 右
  • 可以尝试用三叉链表保存父节点信息
  • p若没有父结点,那就拉倒了

在这里插入图片描述
如果能找到p的父节点

  • 需要分四种情况
  • 第三种情况,p的前驱结点为左兄弟子树中最后一个被前序遍历的节点;
  • 如何找这个结点,在左兄弟子树中优先向右下找,右下没有再向左下找

在这里插入图片描述
后序线索二叉树找后序前驱

  • 有左线索,则返回前驱
  • 没有左线索,则有左孩子;如果有左孩子,则要看是否有右孩子,分情况讨论

在这里插入图片描述
后序线索二叉树找后序后继

  • p若有右指针为后继线索,则直接返回
  • p若有孩子,p的左右子树只能是p的前驱,所以p只能向上找父节点
  • 找p的父结点要么用土办法遍历,要么使用三叉连链表
  • 如果p没有父结点,p是根结点,那就拉倒了

在这里插入图片描述
如果能找到p的父结点

  • 分三种情况
  • 第三种情况,p有右兄弟,则p的后继为右兄弟的第一个被访问的结点
  • 如何找右兄弟第一个点,尽量向左下方向找,左下没有再向右下找

在这里插入图片描述
知识点小结
在这里插入图片描述
在这里插入图片描述

树–存储结构(普通的树)

在这里插入图片描述
树的逻辑结构
在这里插入图片描述
双亲表示法(顺序存储)

  • 新增数据:有一些类似层序,但又不是,无需按逻辑上的次序存储
  • 删除数据
    • 方案一:将结点数据清空,双亲指针设为-1
    • 方案二:尾部的数据移动过来填上
  • 如果删除的是叶子结点,正常删除即可;如果不是叶子结点,意味着该结点后面的孩子结点都删除
  • 如何找孩子?给定结点查双亲结点方便,找孩子只能从头遍历

在这里插入图片描述
在这里插入图片描述
孩子表示法–顺序+链式存储

  • 顺序存储用来存储各个结点的结构体,结构体包含数据本身和孩子指针结构体
  • 孩子指针结构体就是链表结构
  • 思考优缺点,找孩子容易,找双亲麻烦

在这里插入图片描述
孩子兄弟表示法–链式存储–常见考点

  • 左指针指向第一个孩子,右指针指向右兄弟
  • 树转成二叉树
    在这里插入图片描述
    树转成二叉树
    在这里插入图片描述
    森林转成二叉树
    在这里插入图片描述
    在这里插入图片描述
    知识点小结
    在这里插入图片描述

树、森林的遍历

在这里插入图片描述
树的先根遍历

  • 伪代码:先访问根,再依次访问各个孩子,访问各个孩子的时候再进行访问根的递归操作
  • 树的先根遍历的序列和与之对应的二叉树的前序遍历序列是一样的

在这里插入图片描述
树的后根遍历

  • 现访问树的孩子,再访问根
  • 树的后根遍历序列与对应的二叉树的中序遍历序列是相同的

在这里插入图片描述
树的层次遍历–使用辅助队列

  • 与之前的层次遍历一样
  • 探索范围是尽可能的宽,也叫广度优先遍历
  • 先根遍历和后根遍历也称为深度优先遍历

在这里插入图片描述
森林的前序遍历

  • 使用递归
  • 森林的前序遍历效果等同依次对各个子树进行先根遍历的效果
  • 也可以转成二叉树,等同于二叉树的前序遍历

在这里插入图片描述
在这里插入图片描述
森林的中序遍历

  • 效果等同于依次对各个子树进行后根遍历
  • 也可转成二叉树,效果等于对二叉树进行中序遍历

在这里插入图片描述
在这里插入图片描述
知识点小结

在这里插入图片描述

哈夫曼树

在这里插入图片描述
带权路径长度
在这里插入图片描述
哈夫曼树–最优二叉树
在这里插入图片描述
哈夫曼树的构造
在这里插入图片描述
在这里插入图片描述
哈弗曼编码

  • 发送电报运用了哈夫曼树的原理

在这里插入图片描述
如何尽可能减小传输的bit数

  • 构造哈夫曼树
  • 使用可变长度编码

在这里插入图片描述
哈夫曼编码

  • 如果给字符集设计可变长度编码,那么所有的字符在编码树种只能是叶子结点,不可以是分支节点
  • 如果某一字符成为了分支节点,那么解码时就可能产生歧义,因为某一个字符的编码可能成为其他编码的前缀
  • 前缀编码

在这里插入图片描述
哈夫曼编码也不唯一
在这里插入图片描述
哈弗曼编码可以用于数据的压缩
在这里插入图片描述
知识点小结
在这里插入图片描述

并查集–2022新增考点

属于集合类型的逻辑结构
在这里插入图片描述
集合:将元素划分为若干个互不相交的子集
在这里插入图片描述
代码表示集合:

  • 可以将同一个子集中的各个元素,组织成一棵树
  • 如何判断元素属于哪个集合,查根结点
  • 根结点相同的两个元素,属于同一个子集
  • 两个集合合并为一个集合,只需要让一棵树成为另一棵树的子树即可
  • 这就是并和查

在这里插入图片描述
在这里插入图片描述
并查集存储结构

  • 双亲表示法最适合,主要都是在查根结点
  • 并查集的存储结构本质上就是树的双亲表示法

在这里插入图片描述
在这里插入图片描述
并查集代码实现–初始化
在这里插入图片描述并查集代码实现–并和查
在这里插入图片描述
并查集时间复杂度分析

  • 并操作:O(1)
  • 查操作:最坏时间复杂度O(n),与树高相关,如果需要优化,尽量将数变宽

在这里插入图片描述
对并操作的优化–union

  • 让每次的合并都是小树合到大树上
  • 可以用根结点的绝对值,百奥是树的结点总数

在这里插入图片描述
合并操作之后,发现,树高并没有发生变化
在这里插入图片描述
在这里插入图片描述
优化union后,查的操作最坏时间复杂度O(log2n)
在这里插入图片描述
知识点小结
在这里插入图片描述

并查集终极优化

查操作的优化

  • 压缩路径,尽量使得查找路径变短

在这里插入图片描述
优化

  • 查找后发现,BEL都在同一路径上,且最后指向A
  • 可以将他们都指向根结点,这样BEL到根结点都用一次就能到

在这里插入图片描述
查找优化

  • 正常查找根结点
  • 再次查找原路径,将路径上的

在这里插入图片描述
并查集的优化前后对比
在这里插入图片描述

第六章 图

图的基本概念

在这里插入图片描述
图的定义

  • 点集不可以空,边集可以为空

在这里插入图片描述
图的应用

  • 铁路网,交通网
  • 社交软件中的用户关系

无向图,有向图

  • 无向边,有向边
  • 无向边:圆括号表示
  • 有向边:尖括号表示,弧尾,弧头

在这里插入图片描述
简单图,多重图

  • 这里,我们默认探讨简单图

在这里插入图片描述
顶点的度

  • 无向图:顶点的度,依附于该顶点的边的条数
  • 有向图:入度,以该顶点为重点的有向边的数目;出度,以该顶点为起点的有向边的数目
  • 有向图的度,指入度与出度之和

在这里插入图片描述
顶点之间关系描述

  • 路径,回路
  • 简单路径,简单回路
  • 路径长度,顶点到顶点的距离
  • 连通,强连通

在这里插入图片描述
连通图,强连通图考点
在这里插入图片描述
研究图的局部-子图,生成子图
在这里插入图片描述
在这里插入图片描述
连通分量

  • 包含尽可能多的顶点和边的意思,延展到不能再延展了,其实就是独立出来的一个子图
  • 比如全国的铁路网和海南岛的铁路网的关系
    在这里插入图片描述
    强连通分量
  • 类似于连通分量,无向图改成有向图

在这里插入图片描述
生成树

  • 无向图,基于连通图的全部顶点,连成一个极小连通子图,最大限度的减少边的数量

在这里插入图片描述
生成森林

  • 一个非连通图,可以划分为若干个连通分量(极大连通子图),得出他们各自的生成树,这些生成树构成了森林

在这里插入图片描述
边的图,带权图

  • 除了点可以带权,边也可以带权

在这里插入图片描述
无向完全图,有向完全图
在这里插入图片描述
稀疏图,稠密图

  • 没有个明显的界限

在这里插入图片描述

  • 树也是一种特殊的图
  • 考点:多少边必有回路
  • 有向树并不是一个强连通图,因为强连通图要求任意一对顶点之间强连通

知识点小结,常见考点
在这里插入图片描述

图的存储–邻接矩阵法

在这里插入图片描述
邻接矩阵法

  • 画图理解
  • 用二维数组表示矩阵,0表示无,1表示有
  • 无向图是关于主对角线对称的
  • 再用一个一维数组存放顶点信息,顶点在一维数组和在二维数组中的位置是相互对应的

在这里插入图片描述
如何求度,入度,出度
在这里插入图片描述
带权图

  • 没有边用无穷表示
  • 最大的int标识无穷
  • 自己指向自己有时候用无穷,有时候也用0表示

在这里插入图片描述
邻接矩阵性能分析

  • 空间复杂度O(n的平方)
  • 适合存储稠密图
  • 无向图是对称矩阵,也可以用上三角,下三角进行压缩

邻接矩阵法的性质

  • A的n次方,表示矩阵A与矩阵A相乘
  • 可以表示一个点到另一个点再到另一个点的距离
  • n越大表示点越多
  • 不好用语言表述,需要看视频
    在这里插入图片描述
    基于上面的n=2结果,再计算n=3的情况
    在这里插入图片描述
    更严谨的数学方法需要使用离散数学

知识点小结
在这里插入图片描述

图的存储–邻接表法

邻接矩阵表示法

  • 空间复杂度高,适合存储稠密图,不适合存储稀疏图

邻接表

  • 顺序+链式存储

无向图的存储

  • 数组存储各个结点
  • 如果结点有边,就在这个结点后面挂链表,链表的结点里保存的就是边的信息,保存的是指向哪一个结点的信息
  • 这种表示法类似于树的孩子表示法

在这里插入图片描述
在这里插入图片描述
邻接表法,无向图与有向图

  • 有向图:当前结点指向哪里,就是添加一个链表的结点信息
  • 无向图:同一个边会被两个结点都记录下来,因此边的信息有冗余,空间复杂度较高
  • 有向图:每一条弧只会被记录一次
  • 求无向图的度:遍历该结点的边链表即可
  • 求有向图的出度:遍历该结点即可
  • 求有向图的入度:只能遍历所以结点,看哪个结点的边链表里有指向当前结点的边信息,时间复杂度较高
  • 求指向该结点的弧:和找入度类似,也需要遍历所有结点的边链表

在这里插入图片描述
邻接表法表示不唯一

  • 邻接矩阵法表示图是唯一的

在这里插入图片描述
知识点小结
在这里插入图片描述

图的存储–十字链表、邻接多重表

十字链表存储有向图

  • 十字链表只能用于存储有向图
  • 区分顶点结点、弧结点;
  • 作为顶点结点:保存数据,指向外面时对应的弧结点,被外面指向时所对应的弧结点
  • 作为弧结点:保存弧头和弧尾的编号,保存弧头相同的其他弧,保存弧尾相同的其他弧
  • 从一个结点出发
    • 沿着绿色指针往下找,就是找当前顶点的所有发射出去的弧
    • 沿着橙色指针往下找,就是找当前顶点的所有接收过来的弧
  • 空间复杂度也优秀,比较邻接矩阵更节省空间

在这里插入图片描述
在这里插入图片描述
邻接矩阵、邻接表存储无向图
在这里插入图片描述
邻接多重表存储无向图

  • 只适合存储无向图
  • 顶点结点:保存数据,指向第一个边结点(指向编号最小的顶点对应的边)
  • 边结点:存储相连的两个顶点的编号,顶点不分先后,但是要明确;保存两个边结点的指针
  • 边结点里的两个指针要与保存的两个顶点一一对应;
  • 比如,C连接的第一个边结点里面,C是2号橙色,那么找C的下一个边结点的时候,也要通过橙色指针往下找

在这里插入图片描述
邻接多重表优点

  • 一条边只对应一个边结点,无需保存荣誉数据
  • 删除边结点,只需要将其他指向该结点的指针变为空即可
  • 删除顶点,需要删除与该顶点相连的边,在将指向该边结点的指针变为空,再删除顶点信息
  • 空间复杂度很优秀

在这里插入图片描述
知识点小结

  • 不大可能考代码,因为实现代码复杂
  • 重点理解特性

在这里插入图片描述

图的基本操作

在这里插入图片描述
判断图G是否存在边–无向图

  • 邻接矩阵优于邻接表

在这里插入图片描述
判断图G是否存在边–有向图

  • 邻接矩阵优于邻接表

在这里插入图片描述
列出图G中与顶点x临接的边–无向图

  • 邻接矩阵:遍历该顶点的一行或一列
  • 邻接表:遍历该顶点后面的边结点

在这里插入图片描述
列出图G中与顶点x临接的边–有向图

  • 邻接矩阵:遍历该顶点的出边或入边所对应的行或列
  • 邻接表:遍历所有顶点的所有边结点,找到哪些边结点指向当前顶点

在这里插入图片描述
在图G中插入一个顶点x–无向图

  • 邻接矩阵:给原矩阵新增一行和一列,存储数据默认为0应该是初始化时就完成好的
  • 邻接表:在存储顶点的数组中新增一个顶点即可
  • 有向图也类似

在这里插入图片描述
从图G中删除顶点x–无向图

  • 邻接矩阵:如果真的删除一行和一列,意味着后续正大量元素都要跟着移动,不适合;可以给对应的行和列置零,给顶点元素增加一个标记用于表示空顶点;
  • 邻接表:将该顶点在数组中的位置置空,遍历所有其他顶点对应的边结点,如果有指向这个顶点的,也要删除

在这里插入图片描述
从图G中删除顶点x–有向图

  • 邻接矩阵:类似
  • 邻接表:删除顶点后面的结点,遍历所有顶点的结点,找出指向该顶点的结点,并删除

在这里插入图片描述
向图G中添加边–无向图

  • 有向图类似
    在这里插入图片描述
    找到图G中顶点x的第一个临接点–无向图
  • 邻接矩阵:扫描一行直到遇到第一个邻接点
  • 邻接表:顶点连接的第一个边结点就是

在这里插入图片描述
找到图G中顶点x的第一个临接点–有向图

  • 邻接矩阵:出边找行,入边找列,扫描一行或一列直到遇到第一个邻接点
  • 邻接表:找出边,顶点连接的第一个边结点就是;找入边,遍历所有顶点对应的边结点

在这里插入图片描述
给定图G的顶点连接的第一个邻接点,找下一个邻接点–无向图

  • 扫描行或列,找到第二个即可
  • 找顶点,找到后面第二个边结点即可

在这里插入图片描述
给指定的某边或某弧设置权值

  • 重点在于找到这个边是否存在

知识点小结

  • 考试中可能会用到,找顶点的第一个邻接点,第二个邻接点,图的操作中可能会用到
  • 直接调用函数接口也是可以的

在这里插入图片描述

图的遍历–广度优先遍历(BFS)

在这里插入图片描述
树与图的对比在这里插入图片描述
图的广度优先遍历思路

  • 找到与一个顶点相邻的所有顶点
  • 标记哪些顶点被访问过
  • 需要一个辅助队列
  • 上一节有这些基本操作的实现

在这里插入图片描述
图的广度优先遍历代码实现
在这里插入图片描述
小练习
在这里插入图片描述
遍历序列的可变性

  • 邻接矩阵是固定的,遍历序列也固定
  • 邻接表由于边链表不唯一,导致遍历序列可能也不唯一

在这里插入图片描述
算法存在的问题

  • 如果是非联通图,则无法遍历完所有的顶点
  • 解决方案:遍历visited数组,对遇到的第一个false点进行广度优先遍历,最终能确保数组中的所有顶点都被访问
  • 因此,对于无向图,调用BFS函数的次数=连通分量的次数,

在这里插入图片描述

复杂度分析

  • 无需钻到代码最深层计算,换个思路,只需要看要访问多少顶点,访问多少边
  • 没有看for循环的原因,如果是连通分量,则没有循环,但是也访问到了,这也是这个算法的特点
    在这里插入图片描述
    在这里插入图片描述
    广度优先生成树
  • 由广度优先遍历过程确定的
  • 邻接表表示的方式不唯一,因此对应的广度优先生成树也不唯一

在这里插入图片描述
广度优先生成森林

  • 每个连通分量都可以通过遍历对应广度优先生成树
  • 所有广度优先生成树形成一个广度优先生成森林

在这里插入图片描述

练习-广度优先遍历-有向图

  • 思考,能不能只调用一次BFS完成遍历

在这里插入图片描述
知识点小结
在这里插入图片描述

图的遍历–深度优先遍历(DFS)

在这里插入图片描述
回顾树的先根遍历
在这里插入图片描述
图的深度优先遍历

  • 访问的顶点有可能被访问过了,因此需要visit表用来标识
  • 遍历思想类似于树的先根遍历

在这里插入图片描述
算法存在的问题

  • 如果存在多个连通分量,也可能会遍历不完全
  • 解决方法:类似广度的处理,加入扫描visit数组,寻找没有被访问的第一个顶点,然后对其执行DFS函数

在这里插入图片描述
空间复杂度
在这里插入图片描述
时间复杂度
在这里插入图片描述
练习
在这里插入图片描述
基于邻接表表示方式不唯一,对应的深度优先遍历也不唯一
在这里插入图片描述
深度优先生成树

  • 遍历时,一个顶点只需被访问一次,多余的边去掉
  • 邻接表表示方式对应的树也不唯一

在这里插入图片描述
在这里插入图片描述
生成森林

  • 每个连通分量对应一个生成树

在这里插入图片描述
在这里插入图片描述
知识点小结
在这里插入图片描述

最小生成树

在这里插入图片描述
生成树

  • 包含图中全部顶点的一个极小连通子图
  • 顶点数为n,则他的生成树有n-1条边
  • 广度优先遍历和深度优先遍历可以生成不同形态的生成树

在这里插入图片描述
最小生成树(最小代价树)

  • 一个带权的,连通的,无向图可能会有多个生成树
  • 找出权值之和最小的那个生成树
  • 最小生成树可能会有多个
  • 最小生成树的边数=顶点数-1,砍掉一条边就会不连通。增加一条边就会形成回路
  • 如果连通图本身就是一棵树,其最小生成树就是它本身
  • 只有连通图才有生成树,非连通图只能生成森林

在这里插入图片描述
在这里插入图片描述
求最小生成树

  • Prim算法,Kruskal算法,
  • 考察代码的概率不高,重点学习如何手动的实现算法原理

Prim算法(普里姆)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果在选择4的时候,可能会有多个选择,多个结果
也可以从不同的结点出发建立最小生成树
在这里插入图片描述
Kruskal算法(克鲁斯卡尔)
在这里插入图片描述
在这里插入图片描述
两种算法对比
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
知识点小结
在这里插入图片描述

最短路径–BFS算法

在这里插入图片描述
BFS广度优先算法求无权图的单源最短路径
在这里插入图片描述
代码实现

  • 基于广度优先遍历来进行改造
  • d[]数组保存各点到源点的距离,path[]数组保存当前结点的前驱结点

在这里插入图片描述
知识点小结
在这里插入图片描述

最短路径—Dijkstra算法

BFS广度优先遍历算法的局限性

  • 只适用于解决不带权的图

在这里插入图片描述
Dijkstra算法手动执行过程

在这里插入图片描述
得到最短路径的数组,如何使用
在这里插入图片描述
计算机实现–了解

  • 时间复杂度 O(n平方)

在这里插入图片描述
带负权值的图

  • Dijkstra不适用于带负权值的图
  • 什么时候会用到负权值,比如吃鸡中毒圈会掉血,但是医疗包会回血

在这里插入图片描述

最短路径–Floyd算法

Floyd算法

  • 用来求出任意两点之间的最短路径
  • 初始两个矩阵,一个用来记录顶点之间的直接距离,一个记录两个顶点之间的中转顶点
  • 遍历所有顶点,使用算法求出,是否可以再某两个顶点之间增加中转点,可以让路径更短,如果有就更新两个矩阵
  • 这是遍历到各个顶点的情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
经过n轮递推,得到两个矩阵,由此可以找出任意两点的最短距离
在这里插入图片描述
代码实现

  • A就是图的邻接矩阵,path初始都给-1
  • 时间复杂度,空间复杂度
  • 这是王道书中的例子,但是结点只有3个

在这里插入图片描述
结点较多的情况

  • 两个顶点最终可能会经过多个中转点,path上只记录最近的一次,也就是离终点最近的那个中转点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
v4没有任何修改
在这里插入图片描述
根据矩阵找最短路径

  • 通过递方方法,不断找到这个中转点的上一个中转点
  • 实际考试不太可能给这么复杂的图,因为计算量太大,顶多三阶四阶矩阵

在这里插入图片描述
练习

  • 可以解决Dijkstra不能解决的带负权值的图

在这里插入图片描述
不能解决的问题

  • 带有负权回路的图
  • 因为负权路径越多,权值越小

在这里插入图片描述
知识点小结
在这里插入图片描述

有向无环图–描述表达式

有向无环图

  • 不存在环路

在这里插入图片描述
DAG描述表达式

  • 简化重复的计算过程

在这里插入图片描述
在这里插入图片描述
做题总结规律

  • 实际答题中很容出现遗漏,没有合并完全
  • 王道总结的经验,如果合并完全,最中的合并结果是顶点中不可能出现重复的操作数

在这里插入图片描述
解题步骤

  • 列出操作数
  • 标出生效顺序
  • 按顺序加入运算符,注意分层;只有同一层的操作数才是要直接与当前运算符计算的
  • 从底向上逐层检查同一层的运算符是否可以合体,只有运算符和操作数都相同就可以合并

在这里插入图片描述

在这里插入图片描述
合并
在这里插入图片描述
练习
在这里插入图片描述
在这里插入图片描述
如果调换运算的顺序,也可能会得到不同形态的有向无环图

拓补排序

AOV网

  • AOV网一定是个有向无环图

在这里插入图片描述
拓补排序

  • 找到做事的先后顺序
  • 拓补排序的实现
  • 如果原图有回路,则无法使用拓补排序

在这里插入图片描述
基于邻接表的代码实现
在这里插入图片描述
在这里插入图片描述
逆拓补排序
在这里插入图片描述
代码实现

  • 思路与拓补排序相似,只不过找的是出度为0的结点
  • 逆拓补排序推荐使用邻接矩阵,因为邻接表中的边结点找上一个结点需要遍历整个邻接表,时间复杂度大
  • 邻接矩阵只需要遍历一条边即可
  • 也可以使用逆邻接表,每一个顶点后面的边结点,其实是指向当前节点的边

在这里插入图片描述
逆拓补排序的实现–DFS算法

  • 拓补排序–DFS算法后续讲
    在这里插入图片描述
    知识点小结
    在这里插入图片描述

关键路径

AOE网

  • 带权有向图,顶点表示事件,有向边表示活动

在这里插入图片描述
源点,汇点 都只有一个,中间可以有多条路径
在这里插入图片描述
关键路径,关键活动
在这里插入图片描述
从前往后推,最早发生时间
在这里插入图片描述
从后往前推,最迟发生时间

在这里插入图片描述
时间余量

  • 关键路径从前往后推,从后往前推,时间节点都是一样的,没有时间余量,因此不能拖延
    在这里插入图片描述
    求关键路径的步骤
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    知识点小结
  • 考点,求关键路径,关键活动

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值