文章目录

  • 第五章 树与二叉树
    • 树的常考性质
    • 二叉树--定义、基本术语
    • 二叉树--常考性质
    • 二叉树--存储结构
    • 二叉树--先序、中序、后序遍历
    • 二叉树--层序遍历
    • 由遍历序列构造二叉树
    • 线索二叉树--概念
    • 二叉树的线索化--代码实现
    • 线索二叉树--如何找前驱、后继
    • 树--存储结构(普通的树)
    • 树、森林的遍历
    • 哈夫曼树
    • 并查集--2022新增考点
    • 并查集终极优化
  • 第六章 图
    • 图的基本概念
    • 图的存储--邻接矩阵法
    • 图的存储--邻接表法
    • 图的存储--十字链表、邻接多重表
    • 图的基本操作
    • 图的遍历--广度优先遍历(BFS)
    • 图的遍历--深度优先遍历(DFS)
    • 最小生成树
    • 最短路径--BFS算法
    • 最短路径---Dijkstra算法
    • 最短路径--Floyd算法
    • 有向无环图--描述表达式
    • 拓补排序
    • 关键路径

第五章 树与二叉树


树的基本概念

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


树的基本概念

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


树形逻辑结构的应用

结点之间的关系描述

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


结点、树的属性描述

  • 我们默认结点的层次从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算法手动执行过程

  • 文字难以形容,需要看原视频
  • 考试主要考察手动执行原理
  • 强烈推荐看B站视频:https://www.bilibili.com/video/BV1zz4y1m7Nq?spm_id_from=333.337.search-card.all.click


得到最短路径的数组,如何使用

计算机实现–了解

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


带负权值的图

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

最短路径–Floyd算法

Floyd算法

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




经过n轮递推,得到两个矩阵,由此可以找出任意两点的最短距离

代码实现

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


结点较多的情况

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






v4没有任何修改

根据矩阵找最短路径

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


练习

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


不能解决的问题

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


知识点小结

有向无环图–描述表达式

有向无环图

  • 不存在环路


DAG描述表达式

  • 简化重复的计算过程



做题总结规律

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


解题步骤

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


合并

练习


如果调换运算的顺序,也可能会得到不同形态的有向无环图

拓补排序

AOV网

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


拓补排序

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


基于邻接表的代码实现


逆拓补排序

代码实现

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


逆拓补排序的实现–DFS算法

  • 拓补排序–DFS算法后续讲

    知识点小结

关键路径

AOE网

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


源点,汇点 都只有一个,中间可以有多条路径

关键路径,关键活动

从前往后推,最早发生时间

从后往前推,最迟发生时间


时间余量

  • 关键路径从前往后推,从后往前推,时间节点都是一样的,没有时间余量,因此不能拖延

    求关键路径的步骤








    知识点小结
  • 考点,求关键路径,关键活动

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

  1. 数据结构学习笔记(树、二叉树)

    树(一对多的数据结构) 树(Tree)是n(n>=0)个结点的有限集.n=0时称为空树.在任意一颗非空树种: (1)有且仅有一个特定的称为根(Root)的结点: (2)当n>1时,其余结点 ...

  2. 清华邓俊辉数据结构学习笔记(3) - 二叉树、图

    第五章 二叉树 (a)树 树能够结合向量的优点(search)和列表的优点(insert.remove),构成List< List >. 有根树 树是特殊的图 T = (V, E),节点数 ...

  3. 算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS

    图的深搜与广搜 复习下二叉树.图的深搜与广搜. 从图的遍历说起.图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其 ...

  4. 学习笔记:树和二叉树的初步学习1

    树是以分支关系定义的层次结构. 一.树的定义 树是n个结点的有限集.在任意一棵非空树中,有且仅有一个特定的被称为根的结点:当n>1时,其余结点可分为m(m>0)个互不相交的有限集,其中每一 ...

  5. 数据结构学习笔记4 树的基础知识

    树的基本概念 从根结点出发,依次长出各个分支,到达下一级结点,分支又叫做边. 若下一级结点又有新的分支,可以称作分支结点,反之则称为叶子结点. 特别的,空树是结点数为0的树.空树没有根结点,而非空树有 ...

  6. 数据结构学习笔记(第六章:图)

    第六章:图 6.1 图的基本概念 6.2 图的存储及基本操作 邻接矩阵法 邻接表法 十字链表 邻接多重表 6.3 图的遍历 广度优先遍历 深度优先搜索 6.4 图的应用 最小生成树 Prim(普里姆) ...

  7. 数据结构学习笔记(六):二叉树(Binary Tree)

    目录 1 背景知识:树(Tree) 2 何为二叉树(Binray Tree) 2.1 二叉树的概念与结构 2.2 满二叉树与完全二叉树 2.3 二叉树的三种遍历方式 3 二叉树及其遍历的简单实现(Ja ...

  8. 数据结构学习笔记(3-5):树

    附录:所有blog的链接 数据结构学习笔记(1):基本概念 数据结构学习笔记(2):线性结构 数据结构学习笔记(3-5):树 数据结构学习笔记(6-8):图 数据结构学习笔记(9-10):排序 数据结 ...

  9. 《数据结构、算法与应用 —— C++语言描述》学习笔记 — 竞赛树

    <数据结构.算法与应用 -- C++语言描述>学习笔记 - 竞赛树 一.赢者树 二.二叉树的数组描述(补充) 1.声明 2.实现 三.赢者树 1.抽象数据类型 2.赢者树的表示 3.声明 ...

  10. 数据结构学习笔记(七):哈希表(Hash Table)

    目录 1 哈希表的含义与结构特点 1.1 哈希(Hash)即无序 1.2 从数组看哈希表的结构特点 2 哈希函数(Hash Function)与哈希冲突(Hash Collision) 2.1 哈希函 ...

最新文章

  1. 高并发下的接口幂等性解决方案
  2. 看服务器是不是虚拟机
  3. ML之xgboost:基于xgboost(5f-CrVa)算法对HiggsBoson数据集(Kaggle竞赛)训练实现二分类预测(基于训练好的模型进行新数据预测)
  4. 《工作DNA》摘录三
  5. 8000字 | Python数据可视化,完整版实操指南 !
  6. Axure之全局变量
  7. javascript网页自动填表_JavaScript脚本实现网页批量自动勾选及内容填写
  8. python实现归并排序
  9. python 空值(NoneType)
  10. imindmap12新版本 思维导图软件
  11. python如何将批量txt文本转成批量word格式
  12. 【基础教程】禁忌搜索算法【005期】
  13. 智慧水务技能——SWMM、最优化与预测理论及三维动态可视化
  14. 74HC138 三八译码器
  15. 点云应用——三维空间边界点排序+机器人轨迹引导(1)
  16. nexus-3.37.3 报INSTALL4J_JAVA_HOME to point to a suitable JVM
  17. 写教案时,PDF怎么转换成PPT?用迅读PDF大师,超简单
  18. ios 凭据验证_苹果内购服务器验证凭证回执Data
  19. 【线上沙龙】WeTest携手Testerhome共探DevOps下的质量保障
  20. Win11怎么分区硬盘?Win11硬盘分区详细教程

热门文章

  1. 深入场景,智能决策倍增数字化转型价值 | 爱分析报告
  2. 如何计算 N叉树的最大深度
  3. 奥塔在线:MySql数据库定时备份脚本
  4. 《冰雪奇缘》造雪花的技术,被MIT用来开发了一只软体机器人
  5. 云管平台 — vRealize Suite
  6. (free)Windows下的stegdetect下载链接
  7. 【电子通识】烙铁使用方法
  8. Android获取重力加速度和磁场强度
  9. 机器人布罩_铝型材机器人防护罩案例
  10. jz2440 linux4,JZ2440 u-boot-2016.11、linux-4.17和busybox-1.28.4移植笔记