数据结构学习小记-图

  1. 定义:一个图G是一个二元组,即序偶<V,E>,或记作G=<V,E> ,其中V是有限非空集合,称为G的顶点集,V中的元素称为顶点或结点;E称为 G的边的集合,所有的边ei都属于E,都有v中的结点与之对应,称ei为 G的边。

  2. 基本概念:有向图:每条边都是有向边的图;无向图:每条边都是无向边的图;混合图:在一个图中,有些边是有向边,另一些边是无向边,则该图为混合图;有限图:一个图的点集和边集都是有穷集的图;零图:边集为空集的图;平凡图:仅有一个结点而没有边构成的图;关联:若有ei=(u,v)且ei属于E,则称u是v相关联的;孤立点:无边关联的点;

  3. 在任意图中,度数为奇数的点必然有偶数个;所有点入度之和等于出度之和;

  4. 邻接矩阵,形式为bool adj[n][n],这个N是节点个数,adj[i][j]表示i和j之间是否有边;如果边有权值,也可以直接用 int adj[n][n] ,直接把边权存进去。

  5. 邻接表(Adjacency List)顾名思义,就是通过链表或者利用数组模拟链表的方式将图的相连接关系表示的一种方法,存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。这里必须要特别注意邻接表的结尾以空或者一个特殊的标记,表示到达结尾。在一些需要快速表达概念的场合,这里必须要特别注意邻接表的结尾以空或者一个特殊的标记,表示到达结尾。在一些需要快速表达概念的场合,可以将空结点的指向忽略不表达。

  6. 链式向前星代码是基于向前星代码的优化,前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了。

图的遍历-DFS深度优先搜索

  1. 遍历的概念就是:从某一个点出发(一般是首或尾),依次将数据结构中的每一个数据访问且只访问一遍。DFS的具体做法:从某个点一直往深处走,走到不能往下走之后,就回退到上一步,直到找到解或把所有点走完。

  2. 操作动作存储与数据结构(栈)的思想及其相似,同时也由于栈的性质,我们可以通过递归来简化栈的创建,因此DFS算法的两种做法分别是利用栈或者递归实现。

图的遍历之BFS广度优先搜索

  1. 与深度优先算法在一个结点“死磕到底“的思维不同,广度优先算法关注的重点在于每一层的结点进行的下一层的访问。BFS算法和核心思路就是:从某个点一直把其邻接点走完,然后任选一个邻接点把与之邻接的未被遍历的点走完,如此反复走完所有结点。类似于树的层序遍历。BFS的核心就是要把当前在哪作为一个状态存储,并将这个状态交给队列进行入队操作。

最小生成树:普利姆Prim算法

  1. 将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。最小生成树属于一种树形结构(树形结构是一种特殊的图),或者说是直链型结构,因为当n个点相连,且路径和最短,那么将它们相连的路一定是n-1条。可以利用参考一个问题理解最小生成树,有n个村庄,每个村庄之间距离不同,要求村庄之间修路,每一个村庄必须与任意一个村庄联通,如何修路最省钱(修的最短)。

  2. 普算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图。

克鲁斯卡尔算法

  1. 而具体的操作过程为:

    a) 将图的所有连接线去掉,只剩顶点

    b) 从图的边集数组中找到权值最小的边,将边的两个顶点连接起来

    c) 继续寻找权值最小的边,将两个顶点之间连接起来,如果选择的边使得最小生成树出现了环路,则放弃该边,选择权值次小的边

    d) 直到所有的顶点都被连接在一起并且没有环路,最小生成树就生成了。

  2. 两个核心问题

    a.对图的所有边按照权值大小进行排序。

    b.将边添加到最小生成树中时,怎么样判断是否形成了回路。

最短路径-迪杰斯特拉Dijkstra算法

  1. 最短路径问题是图论研究中的一个经典算法问题,旨在寻找图(由结点和路径组成的)中两结点之间的最短路径,大致可以分为如下几种问题,可无论如何分类问题,其本质思想还是不变的,即,求两点间的最短距离。

    a) 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。

    b) 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。

    c) 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。

    d) 全局最短路径问题 - 求图中所有的最短路径。

  2. 迪杰斯特拉算法的核心思路是:

    1. 指定一个节点,例如我们要计算 'A' 到其他节点的最短路径

    2. 引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞)

    3. 初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0,

    4. U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞

    5. 从U集合中找出路径最短的点,加入S集合,例如 A->D = 2

    6. 更新U集合路径,if ( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' ) 则更新U

    7. 循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径

最短路径-弗洛伊德算法Floyd

  1. 弗洛伊德算法的思路是:首先初始化距离矩阵,然后从第一个点开始逐渐更新矩阵点值。d[i][j]表示从i点到j点的距离。第k次更新时,判断d[i][k]+d[k][j]与d[i][j]的大小,如果前者小,则更新这个值,否则不变。这个算法的核心点在于去往每一个点我们所要尽力的每一个点的记录.

  2. 从图的带权邻接矩阵A=[a(i,j)] n×n开始,递归地进行n次更新,即由矩阵D(0)=A,按一个公式,构造出矩阵D(1);又用同样地公式由D(1)构造出D(2);……;最后又用同样的公式由D(n-1)构造出矩阵D(n)。矩阵D(n)的i行j列元素便是i号顶点到j号顶点的最短路径长度,称D(n)为图的距离矩阵,同时还可引入一个后继节点矩阵path来记录两点间的最短路径。

  3. 状态转移方程,其状态转移方程如下: map[i,j]:=min{map[i,k]+map[k,j],map[i,j]};map[i,j]表示i到j的最短距离,K是穷举i,j的断点,map[n,n]初值应该为0或者自定义的特殊意义的点.

posted @   阳扬的杨  阅读(55)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示