图的基本知识

    • 一、图的定义和基本术语
    • 二、图的存储结构
      • (1)数组(邻接矩阵表示法)
      • (2)数组(邻接矩阵)的实现
      • (3)邻接表(链式表示法)
      • (4)邻接表(链式表示法)实现
    • 三、图的遍历
      • (1)深度优先遍历算法
      • (2)广度优先遍历算法
    • 四、图的应用
      • 1、构造最小生成树
        • MST性质
        • 普利姆算法(Prim)
        • 克鲁斯卡尔算法(Kruskal)
      • 2、最短路径
        • 迪杰斯特拉(Dijkstra)
        • 弗洛伊德(Floyd)

一、图的定义和基本术语

图的定义:G=(V,E)

V:顶点(数据元素)的有穷非空集合

E:边的有穷集合

图是包含顶点和边的集合

类似于下图,G1由 V1、V2、V3、V4 四个顶点,四条边组成

G2 由五个顶点,七条边组成。

其中G1中的边带有方向称为有向图, 不带方向的称为无向图

image-20230827130907946

完全图:任意俩个点都有一条边相连

image-20230827131201342

稀疏图: 有很少的边或者弧(有向图的边)比较少的图(n< nlogn)

稠密图: 有较多的边或者弧

: 边/弧 带权的图

邻接: 边/弧相连的俩个顶点之间的关系

<> 表示有向,vi -> vj

image-20230827131439586

顶点的度: 与该顶点相关联的边的数目,记为 TD(v)

在有向图中,顶点的度等于该顶点的入度和出度之和。

顶点 v 的入度是以 v 为终点的有向边的条数记作 ID(v)

顶点 v 的出度是以 v 为始点的有向边的条数 记作 OD(v)

image-20230827131822784

: 当有向图中仅1个顶点的入度为0,其余顶点的入度均为1,此时是何形状?

答:是一颗树,是一颗有向树

image-20230827132034411

路径: 接续的边构成的顶点序列

路径长度: 路径上边或弧的数目/权值之和。

假设从0到2,路径有: 0、3、2, 0、1、2,0、2… ,路径长度分别为:2、2、1…

image-20230827132301601

回路(环): 第一个顶点和最后一个顶点相同的路径

简单路径: 除路径起点和终点可以相同外,其余顶点均不相同的路径

简单回路(简单环): 除路径起点和终点相同外,其余顶点均不相同的路径。

image-20230830225956559

连通图 (强连通图)

在无 (有) 向图G=(V,{E})中,若对任何两个顶点 v、u都存在从v 到 u 的路径,则称G是连通图 (强连通图)

image-20230827132841922

权与网

图中边或弧所具有的相关数称为权。表明从一个顶点到另一个顶点的距离或耗费。带权的图称为网

子图:设有两个图G= (V,{E})、G1= (V1,{E1}),若V1 ∈ V,E1 ∈ E,则称 G1是G的子图

image-20230830230604784

极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边,子图不在连通

生成树:包含无向图G所有顶点的极小连通子图

生成 森林:对于非连通图,由各个连通分量的生成树的集合

image-20230830231052994

抽象数据类型定义

image-20230827134220773

二、图的存储结构

图的逻辑结构:多对多

图没有顺序存储结构但可以借助**二维数组(邻接矩阵)**来表示元素间的关系。

链式存储结构

普通的链式存储无法实现图,因为不知道图中某个顶点到底有多少个前驱和后继。

因此可以使用多重表的方式实现。

image-20230827135049037

(1)数组(邻接矩阵表示法)

建立一个顶点表 (记录各个顶点信息) 和一个邻接矩阵 (表示各个顶点之间关系)

设图A=(V,E)有n个顶点,则

image-20230827140122825

图的邻接矩阵是一个二维数组 :

如果 i 和 j 顶点之间有边或者弧就记为1,否则就记为0

image-20230827140157833

举例说明-无向图的邻接矩阵

v1 与 v2、v4 顶点有边,在二维数组中对应为 arcs[v1][v2]=1, arcs[v1][v4]=1, 其余为 0。

v2与v1、v3、v5顶点有边,在二维数组中对应为 arcs[v2][v1]=1, arcs[v2][v3]=1,arcs[v2][v5]=1其余为 0

以此类推…

image-20230827140357548

分析1: 通过图中我们可以发现,对角线上的值全为0,这是因为顶点与自身之间没有边

分析2: 求第 i 个顶点的度,就是第 i 行值的和

分析3:如果是完全图,也就是说每俩个顶点都有一条边相连,那么除了对角线的值为0,其余都为 1

举例说明-有向图的邻接矩阵

如果某个顶点有 以自身为起点到其他顶点的弧(出度) 那么记为1,否则为0。

例如: 以 v1为起点的有 v2,v3,在二维数组中 arcs[v1][v2]=1、arcs[v1][v3]=1,其余为0,以此类推…

image-20230827141146465

注: 在有向图的邻接矩阵中

第i行含义:以结点vi为尾的弧(即出度边)

第i列含义: 以结点vi为头的弧(即入度边)

分析1: 有向图的邻接矩阵可能是不对称的。

分析2

顶点的出度(以该顶点为起点) = 第i行元素值之和

顶点的入度(以该顶点为终点)=第 i 列元素值之和

顶点的度 = 第i行元素值之和 + 第 i 列元素值之和

举例说明-网的邻接矩阵

如果某个顶点有 以自身为起点到其他顶点的弧 那么记为对应的权值,否则为∞。Wij 表示某个顶点的权值

image-20230827141914211

image-20230827142106326

(2)数组(邻接矩阵)的实现

无向网为例。无向网指:没有方向并且带有权值的图

image-20230827150522937

1、定义存储结构并且进行初始化。初始化时传入一个顶点数组,计算该数组的长度length,邻接矩阵为 length*length的矩阵。并将矩阵全都初始化为最大值

package ChapterSix.graph;import java.util.Arrays;/**** Author: YZG* Date: 2023/8/27 14:24* Description: 实现 无向图的邻接矩阵表示法*/
public class AMGraph {Object[] vexs; // 顶点数组Object[][] arcs; // 邻接矩阵int vexNum, arcNum; // 记录顶点、边的个数/*** @description 初始化* @date 2023/8/27 14:45* @param vexs 表示顶点数组* @return*/public AMGraph(Object[] vexs) {this.vexs = vexs;// 顶点个数int length = vexs.length;this.vexNum = length;this.arcs = new Object[length][length];// 初始化邻接矩阵的值皆为∞ ,在Java就用integer的最大值表示for (int i = 0; i < length; i++) {for (int j = 0; j < length; j++) {arcs[i][j] = Integer.MAX_VALUE;}}}
}

2、根据传入的顶点、权值构建无向网。

    /*** @description 创建无向网* @date 2023/8/27 14:50* @param v1 顶点1* @param v2 顶点2* @param weight 顶点1和顶点2之间的权值* @return void*/public void createUDN(Object v1, Object v2, int weight) {// 找到v1、v2的下标int i = findIndex(vexs, v1);int j = findIndex(vexs, v2);// 防止输入错误if (i == -1 || j == -1) throw new RuntimeException("您输入顶点有误");// 赋值权值,因为是无向图,所以反向的权值也要赋arcs[i][j] = weight;arcs[j][i] = weight;// 边的个数+1this.arcNum++;}

3、由于传入的是顶点的名称,还需要一个方法用来找到顶点的下标。

/*** @description 根据顶点名称找到对应的下标* @date 2023/8/27 14:51* @param vexs 顶点数组* @param v 顶点名称* @return int*/
public int findIndex(Object[] vexs, Object v) {for (int i = 0; i < vexs.length; i++) {if (vexs[i]==v) return i;}return -1;
}

测试

    public static void main(String[] args) {AMGraph amGraph = new AMGraph(new Object[]{"v1", "v2", "v3","v4"});// 增加边amGraph.createUDN("v1","v2",1);amGraph.createUDN("v1","v3",2);amGraph.createUDN("v1","v4",3);amGraph.createUDN("v3","v4",4);System.out.println(Arrays.deepToString(amGraph.arcs));System.out.println("边的个数:" + amGraph.arcNum);}

总结

无向图、有向网 都一样。只不过邻接矩阵存储的数据不一样

无向图:没有权值了,因此在arcs初始化时皆为0,在赋值的时候赋为1

         // 无向图-初始化arcs[i][j] = 0; // 无向图-赋值arcs[i][j] = arcs[j][i] = 1;

有向网:只需要赋一次权值即可,无需设置反向

 arcs[i][j] = weight;

邻接矩阵的优点

  • 直观、简单、好理解
  • 方便检查任意一对顶点间是否存在边
  • 方便找任一顶点的所有“接点”(有边直接相连的顶点)
  • 方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)
    • 无向图: 对应行(或列)非0元素的个数
    • 有向图: 对应行非0元素的个数是"出度", 对应列非0元素的个数是"入度"

缺点

  • 不方便增加和删除顶点
  • 浪费空间,例如存储稀疏图(点很多但是边很少)有大量无效元素
  • 浪费时间,统计稀疏图中一共有多少条边

邻接矩阵的方式和边的个数没有关系,只和顶点的个数有关,存储空间:O(n2

(3)邻接表(链式表示法)

邻接表的表示方法仍然需要一个顶点表,但与邻接矩阵的顶点表不同的是,这个顶点表中元素的类型是一个结点

data用来存放顶点的信息,firstarc 用来存储第一个边结点的地址,也就是说与data相连的顶点。

image-20230827153351665

邻接表中仍然使用一个结点来表示俩个顶点的关系

adjvex 用来表示当前顶点的地址,nextarc表示下一个边顶点的地址,因此对于某一个顶点来说有几个相连的顶点就有几个结点

image-20230827153907158

如果存储网结果,就在多加一个链域用于存储权值

image-20230827154457111

案例

对于v1顶点来说,与它相邻的顶点有 v4,v2,在顶点表中对应的下标为 3、1

image-20230827154149934

特点

  • 邻接表不唯一,对于相连的顶点可以更改顺序
  • 若无向图中有 n 个顶点、e条边,则其邻接表需 n 个头结点和2e表结点。适宜存储稀疏图
  • 无向图中顶点 vi 的度为第i个单链表中的结点数

存储空间为:O(n+2e)

有向图-邻接表演示

在有向图中只保存以该顶点为起点的弧(出边)的顶点

例如:以v1为起点的弧的顶点为 v2、v3,对应下标 1,2

image-20230828211429792

特点

  • 顶点为Vi 的出度为第 i 个单链表中的结点个数
  • 顶点 Vi 的入度为整个单链表中邻接点域值是 i -1 的结点个数

找出度易,入度难

(4)邻接表(链式表示法)实现

以无向网为例

1、定义 顶点、边顶点和图的存储结构

public class ALGraph {// 存储所有顶点的数组VNode[] vertices;// 顶点数、边数int vexNum,arcNum;
}// 定义顶点结构
class VNode{// 顶点信息Object data;// 指向第一条边顶点的指针ArcNode firstarc;@Overridepublic String toString() {return "VNode{" +"data=" + data +", firstarc=" + firstarc +'}';}
}// 边顶点类型
class ArcNode{// 边顶点的索引位置int adjvex;// 下一个边顶点的地址ArcNode nextarc;// 顶点信息Object info;@Overridepublic String toString() {return "ArcNode{" +"adjvex=" + adjvex +", nextarc=" + nextarc +", info=" + info +'}';}
}

2、初始化,将顶点信息存储在顶点表,并初始化头指针为NULL

public class ALGraph {public static void main(String[] args) {ALGraph alGraph = new ALGraph(new Object[]{"A","B","C","D"});System.out.println(Arrays.toString(alGraph.vertices));}// 存储所有顶点的数组VNode[] vertices;// 顶点数、边数int vexNum,arcNum;// 初始化 vnodes==顶点集合public ALGraph(Object[] vnodes) {this.vexNum = vnodes.length;this.vertices = new VNode[this.vexNum];this.arcNum = 0;// 将头顶点赋值,指向第一个边为nullfor (int i = 0; i < this.vexNum; i++) {VNode vNode = new VNode();vNode.data = vnodes[i];vNode.firstarc = new ArcNode();this.vertices[i] = vNode;}}
}// 定义顶点结构
class VNode{// 顶点信息Object data;// 指向第一条边顶点的指针ArcNode firstarc;@Overridepublic String toString() {return "VNode{" +"data=" + data +", firstarc=" + firstarc +'}';}
}// 边顶点类型
class ArcNode{// 边顶点的索引位置int adjvex;// 下一个边顶点的地址ArcNode nextarc;// 顶点信息Object info;@Overridepublic String toString() {return "ArcNode{" +"adjvex=" + adjvex +", nextarc=" + nextarc +", info=" + info +'}';}
}

3、给定顶点和边的权值生成邻接表

    // 生成邻接表 v1 —— v2public void createALGraph(Object v1,Object v2,int weight) {// 找到俩个顶点的位置int i = findIndex(v1);int j = findIndex(v2);// 生成新的边顶点ArcNode arcNode = new ArcNode();arcNode.adjvex = j;arcNode.nextarc = vertices[i].firstarc;arcNode.info = weight;vertices[i].firstarc = arcNode;// 由于是无向网,反向也得连接ArcNode arcNode1 = new ArcNode();arcNode1.adjvex = i;arcNode1.nextarc = vertices[j].firstarc;arcNode1.info = weight;vertices[j].firstarc = arcNode1;}/*** @description 根据顶点名称找到对应的下标* @date 2023/8/27 14:51* @param v 顶点名称* @return int*/public int findIndex(Object v) {for (int i = 0; i < vertices.length; i++) {if (vertices[i].data == v) return i;}return -1;}

总结:

邻接矩阵与邻接表的关系

image-20230828223437687

联系:

无论是邻接矩阵还是邻接表,第 i 行都代表 第 i 个顶点与其他顶点的关系。

区别

对于任一确定的无向图,邻接矩阵是唯一的 (行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)

邻接矩阵的空间复杂度为O(n2) , 邻接表的空间复杂度为O(n+e)

用途

邻接矩阵多用于稠密图,而邻接表多用于稀疏图

三、图的遍历

从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。

image-20230828230607258

图的特点

图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点

怎么避免重复访问呢?

可以设置一个辅助数组 visited[n] ,用来表示被访问过的顶点,初始都为false,如果第 i 个顶点被访问,设置 visited[i] = true

图的遍历方法

  • 深度优先搜索 (Depth First Search-DFS )
  • 广度优先搜索 ( Breadth Frist Search-BFS)

(1)深度优先遍历算法

image-20230828231527795

案例演示

V1 =》V2 =》 V4 =》 V8 =》 V5 ,发现走不通了回退到 V8 ,仍然没有可以访问的顶点,继续回退

回退到 V1 =》V3 =》V6 =》 V7

image-20230828231635288

深度优先遍历算法实现

以无向网为例,如下图所示,按照深度优先遍历

image-20230829221434660

假设从 v1 出发,与之邻接的第一个顶点为 v2,在 visited 数组中发现 v2 并没有被访问过,因此访问 v2,并修改 v2 的访问状态

image-20230829221918512

访问完 v2,从邻接矩阵中看出,与之邻接的顶点为v1,但是 v1 已经被访问过。回退到 v1,访问下一个邻接顶点 v3,并修改访问状态。

image-20230829222103872
最后访问v4,结束遍历!

代码实现: 完整代码,包括无向网的创建

public class AMGraph {public static void main(String[] args) {AMGraph amGraph = new AMGraph(new Object[]{"v1", "v2", "v3", "v4"});// 增加边amGraph.createUDN("v1", "v2", 1);amGraph.createUDN("v1", "v3", 2);amGraph.createUDN("v1", "v4", 3);amGraph.createUDN("v3", "v4", 4);System.out.println(Arrays.deepToString(amGraph.arcs));// System.out.println("边的个数:" + amGraph.arcNum);// 从v1开始深度遍历amGraph.DFS(0);}Object[] vexs; // 顶点数组Object[][] arcs; // 邻接矩阵int vexNum, arcNum; // 记录顶点、边的个数// 辅助数组,记录顶点是否被访问boolean[] visited;/*** @description 初始化* @date 2023/8/27 14:45* @param vexs 表示顶点数组* @return*/public AMGraph(Object[] vexs) {this.vexs = vexs;// 顶点个数int length = vexs.length;this.vexNum = length;this.arcs = new Object[length][length];this.visited = new boolean[length];// 初始化访问数组Arrays.fill(visited, false);// 初始化邻接矩阵的值皆为∞ ,在Java就用integer的最大值表示for (int i = 0; i < length; i++) {for (int j = 0; j < length; j++) {arcs[i][j] = Integer.MAX_VALUE;// 无向图// arcs[i][j] = 0;}}}/*** @description 创建无向网* @date 2023/8/27 14:50* @param v1 顶点1* @param v2 顶点2* @param weight 顶点1和顶点2之间的权值* @return void*/public void createUDN(Object v1, Object v2, int weight) {// 找到v1、v2的下标int i = findIndex(vexs, v1);int j = findIndex(vexs, v2);// 防止输入错误if (i == -1 || j == -1) throw new RuntimeException("您输入顶点有误");// 赋值权重,因为是无向图,所以反向的权值也要赋arcs[i][j] = weight;arcs[j][i] = weight;// 无向图// arcs[i][j] = arcs[j][i] = 1;// 有向网// arcs[i][j] = weight;// 边的个数+1this.arcNum++;}/*** @description 根据顶点名称找到对应的下标* @date 2023/8/27 14:51* @param vexs 顶点数组* @param v 顶点名称* @return int*/public int findIndex(Object[] vexs, Object v) {for (int i = 0; i < vexs.length; i++) {if (vexs[i] == v) return i;}return -1;}/*** @description 深度优先遍历算法* @date 2023/8/29 22:22* @param v 访问的顶点下标* @return void*/public void DFS(int v) {// 访问当前顶点System.out.println(vexs[v]);// 更改访问记录值visited[v] = true;// 访问邻接顶点for (int i = 0; i < vexs.length; i++) {// 该邻接顶点没有 被访问过if (((int) arcs[v][i]) != Integer.MAX_VALUE && !visited[i]) {// 递归访问DFS(i);}}}
}

(2)广度优先遍历算法

从图的某一结点出发,首先依次访问该结点的所有邻接点v1、v2、…vn ,在按这些顶点被访问的先后次序依次访问与他们相邻接的所有未被访问的顶点。

重复此过程,直到所有顶点均被访问为止!

image-20230829223510230

算法演示

利用邻接表+队列实现广度优先遍历算法

广度优先算法其实和树的层次遍历有些类似,都是一层一层的遍历,因此我们仍然利用 队列 来实现。

以上面那个图为例,求出它的邻接表,如下图所示:

image-20230830221422220

初始化访问数组:

image-20230830221647744

初始化队列:

image-20230830221717224

1、假设我们从 v1 开始,v1 没有被访问,那么将 v1 结点对应的下标入队,同时标记为已访问,标记完,出队进行访问。

image-20230830222210197

2、访问完 v1,通过邻接表,找到 与 v1 相连接弧的下标为 1,2 ,对应的结点 v2,v3 ,v2、v3没有被访问, 将 v2,v3的下标入队。此时队列的情况:

image-20230830223734594

3、入队之后,首先判断 v2 是否被访问过,发现没有则进行标记,然后将 v2 出队访问

image-20230830224148256

4、v2 出队之后,继续寻找与 v2 相邻接的弧,通过邻接表发现有:0,3,4 对应的结点为:v1、v4、v5,发现 v1 被访问了,v4、v5没有被访问,那么将 v4,v5的下标入队。此时队列情况:

image-20230830224555694

5、入队之后,v3没有被访问,进行标记,然后将v3出队访问

image-20230830224803678

6、不断执行上面的操作:找到出队结点的邻接弧 —— 若没有访问过则入队 —— 进行标记 —— 出队访问,直到队列为空。

代码实现

顶点结构

// 定义顶点结构
class VNode {// 顶点信息Object data;// 指向第一条边顶点的指针ArcNode firstarc;@Overridepublic String toString() {return "VNode{" +"data=" + data +", firstarc=" + firstarc +'}';}
}

边顶点存储结构

// 边/弧 顶点类型
class ArcNode {// 边顶点的索引位置int adjvex;// 下一个边顶点的地址ArcNode nextarc;// 顶点信息Object info;@Overridepublic String toString() {return "ArcNode{" +"adjvex=" + adjvex +", nextarc=" + nextarc +", info=" + info +'}';}
}

图的存储结构

public class ALGraph {// 存储所有顶点的数组VNode[] vertices;// 顶点数、边数int vexNum, arcNum;// 辅助数组,记录顶点是否被访问boolean[] visited;// 初始化 vnodes==顶点集合public ALGraph(Object[] vnodes) {this.vexNum = vnodes.length;this.vertices = new VNode[this.vexNum];this.arcNum = 0;// 将头顶点赋值,指向第一个边为nullfor (int i = 0; i < this.vexNum; i++) {VNode vNode = new VNode();vNode.data = vnodes[i];vNode.firstarc = new ArcNode();this.vertices[i] = vNode;}// 初始化访问数组visited = new boolean[this.vexNum];Arrays.fill(visited, false);}// 生成邻接表 v1 —— v2public void createALGraph(Object v1, Object v2, int weight) {// 找到俩个顶点的位置int i = findIndex(v1);int j = findIndex(v2);// 生成新的边顶点ArcNode arcNode = new ArcNode();arcNode.adjvex = j;arcNode.nextarc = vertices[i].firstarc;arcNode.info = weight;vertices[i].firstarc = arcNode;// 由于是无向网,反向也得连接ArcNode arcNode1 = new ArcNode();arcNode1.adjvex = i;arcNode1.nextarc = vertices[j].firstarc;arcNode1.info = weight;vertices[j].firstarc = arcNode1;}/*** @description 根据顶点名称找到对应的下标* @date 2023/8/27 14:51* @param v 顶点名称* @return int*/public int findIndex(Object v) {for (int i = 0; i < vertices.length; i++) {if (vertices[i].data == v) return i;}return -1;}

广度优先遍历算法实现

LinkedList 为双向循环的队列,addLast 将元素插入队尾(入队),poll 获取对头元素并删除(出队)

  /*** @description 广度优先遍历* @date 2023/8/29 22:52* @param* @return void*/public void BFS(int v) {// 使用LinkedList模拟循环队列LinkedList<Integer> queue = new LinkedList<>();// 修改当前顶点访问状态visited[v] = true;// 将当前顶点插入队尾queue.addLast(v);while (!queue.isEmpty()) {// 出队Integer w = queue.poll();// 找到w顶点的第一条弧ArcNode firstarc = vertices[w].firstarc;// 访问System.out.println(vertices[w].data);// 循环找到与w顶点相邻接的弧while (firstarc.nextarc != null) {// 弧结点的下标int adjvex = firstarc.adjvex;// 判断是否访问过if (!visited[adjvex]) {// 没有访问过,直接入队queue.addLast(adjvex);// 标记visited[adjvex] =  true;}// 移动下一个弧的结点firstarc = firstarc.nextarc;}}}

四、图的应用

1、构造最小生成树

生成树: 所有顶点均由边连接在一起,但不存在回路的图

image-20230831143709148

特点

  • 生成树的顶点个数与图的顶点个数相同
  • 生成树是图的极小连通子图,去掉一条边则非连通
  • 一个有n个顶点的连通图的生成树有 n-1 条边,反之则不一定
  • 在生成树中再加一条边必然形成回路

无向图的生成树

我们可以利用图的遍历生成最小生成树,将访问结点走过的边加到生成树当中

image-20230831144535336

最小生成树: 给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树
也叫最小代价生成树

image-20230831145014503

最小生成树的典型用途

欲在n个城市间建立通信网,则n个城市应铺n-1条线路

但因为每条线路都会有对应的经济成本,而n个城市最多有n(n-1)/2条线路,那么,如何选择n-1条线路,使总费用最少?

此问题我们就可以转化为求最小生成树,n个城市看做n个顶点,线路看做边,经济成本看做权值。

MST性质

构造最小生成树的算法很多,其中多数算法都利用了MST的性质

MST 性质: 设N =(V E) 是一个连通网,U 是顶点集V的一个非空子集。若边(u,v) 是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树

image-20230831150601189

MST性质解释

在生成树的构造过程中,图中n个顶点分属两个集合:

  • 已落在生成树上的顶点集: U
  • 尚未落在生成树上的顶点集: V-U

接下来则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边

image-20230831151037365

普利姆算法(Prim)

算法思想

设 N=(V,E) 是连通图,TE是N上最小生成树中边的集合。

初始令 U={u0} ,u0 ∈ V,TE={}

  • 在所有 U 与 V-U 的边中,找到一条权值最小的边 (u0,v0)
  • 将 (u0,v0) 加入到 TE中, 同时 v0 并入 U
  • 重复上面操作,直到 U=V位置,则 T=(V, TE) 为N的最小生成树

算法演示

1、假设从 V1 开始,U = {V1} V-U= {V2,V3,V4,V5,V6} 相连且权值最小的边为 V1-V3,将 V3 加入到 U, 并且将边加入到 TE 中。

U = {V1, V3},TE={(V1,V3)}

image-20230831153405949

2、U = {V1,V3} V-U= {V2,V4,V5,V6} 相连且权值最小的边为 V3-V6

将 V6 加入到 U 中,相应的边加入到 TE 中。

U = {V1, V3,V6},TE={(V1,V3), ((V3,V6))}

image-20230831153337674

3、重复以上操作,直到 U = V

image-20230831153430406

克鲁斯卡尔算法(Kruskal)

设连通网 N=(V,E),令最小生成树初始状态为只有 n个顶点而无边的非连通图T=(V,{})每个顶点自成一个连通分量

image-20230831155901689

在E中选取代价最小的边(对边按权值大小升序),若该边依附的顶点落在T中不同的连通分量上(即:不能形成环)则将此边加入到 T中;否则,舍去此边,选取下一条代价最小的边

image-20230831160809445

俩种算法的比较

image-20230831161513434

2、最短路径

典型用途:交通网络的问题一从甲地到地之间是否有公路连通?在有多条通路的情况下,哪一条路最短?

那么交通网络用有向图来表示,顶点表示地点,俩个地点的连通用弧表示,权值表示俩地之间的距离。

问题抽象: 在有向网中A点(源点)达 B 点(终点)的多条路径中,寻找一条各边权值之和最小的路径,即最短路径

最短路径与最小生成树不同,路径上不一定包含 n个顶点,也不-定包含 n-1条边

第一类问题: 俩点间的最短路径——迪杰斯特拉(Dijkstra)算法

image-20230831162503544

第二类问题: 某源点到其他各个顶点的最短路径——通常使用弗洛伊德—Floyd算法求解

image-20230831162757912

迪杰斯特拉(Dijkstra)

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个结点到其他结点的最短路径。 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

算法步骤

  • 初始化: 先找出从源点V0,到各终点V的直达路径 (V0,Vk),即通过一条弧到达的路径
  • 选择: 从这些路径中找出一条长度最短的路径 (V0,U)
  • 更新: 然后对其余各条路径进行适当调整
    • 若在图中存在弧 (U,Vk) ,且 (V0,U) + (U,Vk) < (V0,Vk)则以路径 V0,U,Vk) 代替 (V0,Vk)
    • 依此类推在调整后的各条路径中,再找长度最短的路径

迪杰斯特拉 (Dijkstra)算法: 按路径长度递增次序产生最短路径

1、把V分成俩组

  • S:已求出最短路径的顶点的结合
  • T = V-S : 尚未确定最短路径的顶点集合

2、将 T 中顶点按最短路径递增的次序加入到 S 中

  • 保证从源点到 S 中各顶点的最短路径都不大于 源点到T中任何顶点的最短路径长度。

算法演示

初始 S= {V0} , T = {其余顶点}

T中顶点对应的距离值用辅助数组D存放,若有直达的路径,则存储距离值,若不存在则为∞

image-20230901153005912

image-20230901153025559

1、从 V0 开始,找到能够直达的顶点有:V2、V1、V6、V4,其余顶点的距离皆为 ∞

image-20230901162301930

2、在这些直达路径中,找到最短的路径的顶点,加到 S 中。此时 S = {V0 , V2}

T = {V1 , V3 , V4 , V5, V6} ,

image-20230901163939942

3、 加入 V2 顶点后,以 V2 顶点作为中间顶点,若V0 距离这些顶点是否变短了,就更新表中的距离。

以 V3 为例 ,未加入 V2 之前是∞ ,加入之后,路径为 13 ,就更新表中的路径为 13…以此类推

在更新后的路径中,继续找最短的路径,并将顶点加入到 S 中

此时 S = {V0 ,V1 , V2} , T = { V3 , V4 , V5, V6} ,

image-20230901164446131

3、重复上面的操作,直到 S=V,找到所有的顶点即可。

弗洛伊德(Floyd)

求所有顶点间的最短路径:

方法一:每次以一个顶点为源点,重复执行 Dijkstra 算法

方法二: 弗洛伊德算法

算法思想

逐个顶点试探

从vi到vj的所有可能存在的路径中,选出一条长度最短的路径

案例演示

image-20230904114142677

1、初始时设置一个邻接矩阵表示图,存在弧为 权值,否则为 ∞ ,对角线为0

image-20230904113332752

2、逐步试着在原直接路径中增加中间顶点,若加入中间顶点后路径变短,则修改之; 否则,维持原值。所有顶点试探完毕,算法结束

(1)加入A顶点, A——B、C都没有变化,B——A、C也没有变化,C —— B,由于A点的加入,变为可达,C-A-B,路径为7,更新表中的权值

image-20230904113811350

(2)加入B顶点后,A-C路径为 11,加入B顶点,A-B-C 路径为 6,比原来路径小,更新表中的权值

image-20230904114116831

(3)加入C后,B-A 变成了 B-C-A,路径变为5

image-20230904114342852

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/132527.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

释放潜能!RunnerGo:性能测试的全新视角

在数字化时代&#xff0c;性能测试已成为企业持续发展的关键一环。但面对繁杂的工具和流程&#xff0c;很多企业却陷入了无从选择的困境。现在&#xff0c;一款名为RunnerGo的全新性能测试工具正悄然崭露头角。 RunnerGo&#xff0c;一款由国内开发者自主研发的全栈式性能测试…

模拟实现C语言--strlen函数

模拟实现C语言–strlen函数 模拟实现C语言--strlen函数一、strlen函数是什么&#xff1f;二、strlen函数的模拟实现2.1 计数器方式实现strlen函数2.2 不创建临时变量计数器方式实现strlen函数2.3 指针-指针方式实现strlen函数 三、strlen函数的返回类型 一、strlen函数是什么&a…

使用Spring Gateway为对象存储系统MinIo和kkFileView文档预览增加登录验证

文章目录 1、kkfileview下载部署1.1、安装包部署运行1.1.1、物理机或虚拟机上运行1.1.2、Docker容器环境环境运行 1.2、接入说明 2、使用Spring Gateway增加登录认证2.1、网关实现代码2.2、文件服务实现代码2.3、Demo运行效果 官网介绍&#xff1a;kkFileView为文件文档在线预览…

易点易动固定资产管理系统:为企业提供高效资产管理的必备利器

在如今竞争激烈的商业环境中&#xff0c;企业对于固定资产的管理显得尤为重要。然而&#xff0c;传统的手工管理方式已经无法满足企业对于高效、准确、智能化管理的需求。因此&#xff0c;引入一款先进的固定资产管理系统是必不可少的。易点易动固定资产管理系统作为一款领先的…

C#自定义控件组件实现Chart图表(多Y轴,选择图例加粗,选择放大,缩放,点击查看信息等功能)

先看看ECharts的效果 C# 工具箱里的Chart控件就不演示了,很多效果没办法做出来,做出来效果也很不理想。所以,需要自己去手动实现工具箱里的Chart没办法实现的效果; 先看看实现后的效果 绑定数据 点击图表 点击右侧图例加粗 选择放大 右键 点击缩小,恢复

重建大师提交空三后引擎状态是等待,怎么开启?

答&#xff1a;图片中这是在自由网空三阶段&#xff0c;整个AT都是等待中&#xff0c;可以修改任务目录和监控目录看一下&#xff0c;先设置引擎&#xff0c;再提交空三。

建站系列(三)--- 网络协议

目录 相关系列文章前言一、定义二、术语简介三、协议的组成要素四、网络层次划分五、常见网络协议划分六、常用协议介绍&#xff08;一&#xff09;TCP/IP&#xff08;二&#xff09;HTTP协议&#xff08;超文本传输协议&#xff09;&#xff08;三&#xff09;SSH协议 相关系列…

DC/DC开关电源学习笔记(五)开关电源的主要技术指标

(五)开关电源的主要技术指标 1.输入参数2.输出参数3.效率4.电压调整率和负载调整率5.动态特性:负载突变时输出电压的变化6.电源启动时间(Set-Up Time)与保持时间(Hold-Up Time)1.输入参数 输入电压大小,交流还是直流,相数,频率等。 2.输出参数 输出功率,输出电压,输出…

算法-26. 删除有序数组中的重复项-⭐

给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你需要做…

2023-9-10 集合-Nim游戏

题目链接&#xff1a;集合-Nim游戏 #include <iostream> #include <cstring> #include <algorithm> #include <unordered_set>using namespace std;const int N 110, M 10010;int n, m; int s[N], f[M];int sg(int x) {if(f[x] ! -1) return f[x];//…

NeuroFlash:AI文章写作与生成工具

【产品介绍 ​ 】 名称 NeuroFlash 上线时间 2015 具体描述 Neuroflash是一款基于人工智能的文本和图像生成器&#xff0c;可以帮助用户快速创建高质量的内容。Neuroflash拥有超过100种短文和长文的文本类型&#xff0c;涵盖了各种营销场景和需求。只需要输入简单的指示&#…

云计算与虚拟化

一、概念 什么是云计算&#xff1f; 云计算&#xff08;cloud computing&#xff09;是分布式计算的一种&#xff0c;指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序&#xff0c;然后&#xff0c;通过多部服务器组成的系统进行处理和分析这些小程序得到结果…

【LeetCode】210. 课程表 II——拓扑排序

题目链接&#xff1a;210. 课程表 II 题目描述&#xff1a; 现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在选修课程 ai 前 必须 先选修 bi 。 例如…

gmssl v2 用 dgst 命令通过 sm2 签名出的结果,在别的工具上无法验签的问题分析

结论 通过分析发现&#xff0c;导致问题的原因是&#xff1a;gmssl v2 调用的算法不是 sm2 算法。 分析详情 具体情况如下所述 在 gmssl 调用 pkey_ec_init 函数时&#xff0c;默认会把 ec_scheme 设置为 NID_secg_scheme 签名的过程中会调用 pkey_ec_sign 函数&#xff0c…

29.Xaml TreeView控件---->树形控件,节点的形式显示数据

1.运行效果 2.运行源码 a.Xaml源码 <Window x:Class="testView.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mic…

删除linux(centos7)系统自带的open jdk,安装配置jdk环境

查看jdk版本 安装的linux自带jdk8版本&#xff0c;我们不用自带的。 安装jdk步骤 1、下载 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads 2、创建目录 创建文件夹&#xff0c;用来部署JDK&#xff0c;将JDK安装部署到&#xff1a;/export/se…

云原生Kubernetes:pod资源管理与配置

目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略 4.pod 的重启策略 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.格式错误 4.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是kubernetes中…

Matlab学习-自定义函数

Matlab学习-自定义函数 常用自定义函数 文章目录 Matlab学习-自定义函数1. 打印时间2. 计算统计参数3. 画图函数 1. 打印时间 function result calculate_time(time)% Function describe : calculate time% Input : time:N*1% Output : result.hour/min/sec hour/min/sec…

中断子系统 --- 硬件相关层

中断控制器驱动 由于linux支持中断控制器的级联&#xff0c;因此多个中断控制器就可能包含相同的硬件中断号。为了可以通过中断号唯一地标识一个中断&#xff0c;linux引入了irq domain机制以实现将硬件中断号映射为全局唯一的逻辑中断号。 Hwirq映射到irq 每个中断控制器都对…

Day59|leetcode 503.下一个更大元素II、42. 接雨水

leetcode 503.下一个更大元素II 题目链接&#xff1a;503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;单调栈&#xff0c;成环了可怎么办&#xff1f;LeetCode&#xff1a;503.下一个更大元素II_哔哩哔哩_bilibili 题目概述 给定一个循环…