实例代码下载链接
邻接矩阵(Adjacency Matrix)实例代码
通过网盘分享的文件:testAMGraph.rar
链接: https://pan.baidu.com/s/1DMbdjZvpSOsVQOvh-If5yQ?pwd=w2t1 提取码: w2t1
邻接表(Adjacency List)实例代码
通过网盘分享的文件:testALGraph.rar
链接: https://pan.baidu.com/s/1DSyU5XqYvRVoVoU3-raUOw?pwd=py1c 提取码: py1c
十字链表(Orthogonal List)实例代码
通过网盘分享的文件:testOLGraph.rar
链接: https://pan.baidu.com/s/12tnDyQN8snQjAJrKtS68Ig?pwd=mf1t 提取码: mf1t
邻接多重表(Adjacency Multilist)实例代码
通过网盘分享的文件:testAMLGraph.rar
链接:https://pan.baidu.com/s/1xb8WU8_gN8iCy3jkRglc-w?pwd=f6ry 提取码: f6ry
边集数组(Edgeset Array)实例代码
通过网盘分享的文件:testEAGraph.rar
链接:https://pan.baidu.com/s/1AQkz1aOXopGUr8w7s8DXTA?pwd=861z 提取码: 861z
一、图(Graph)
1、图的定义
图(Graph)是由顶点的有穷非空集合V(G)和顶点之间边的集合E(G)组成。
通常表示为:G=(V,E)。其中,G表示个图,V是图G中顶点的集合,E是图G中边的集合。
线性表可以是空表,树可以是空树,但图不可以是空图。
在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。
图可以用来表示很多现实世界中的关系,比如社交网络、地图路线、网络拓扑等。
基本术语:
顶点(Vertex):图中的基本单元,也称为节点。
边(Edge):连接两个顶点的线段,也称弧。
度(Degree):与某个顶点相连的边的数量。
路径(Path):从一个顶点到另一个顶点的顶点序列。
环(Cycle):起点和终点相同的路径
2、图的基本概念
1)、有向图
若E是有向边(也称弧)的有限集合时,则图G为有向图。弧是顶点的有序对,记为<v, w>,其中v,w是顶点,v称为弧尾,w称为弧头,<v,w>称为从顶点v到顶点w的弧,也称v邻接到w,或w邻接自v。

图(a)所示的有向图G 1 可表示为:
G 1 = ( V 1 , E 1 )
V 1 = { 1 , 2 , 3 }
E 1 = { < 1 , 2 > , < 2 , 1 > , < 2 , 3 > }
边有方向,
(
v
,
w
)
(v,w)
(v,w)和
(
w
,
v
)
(w,v)
(w,v)是不同的边。
2)、无向图
若E是无向边(简称边)的有限集合时,则图G为无向图。边是顶点的无序对,记为(v, w)或(w,v),因为(v,w)=(w,v), 其中v,w是顶点。可以说顶点w和顶点v互为邻接点。边(v, w)依附于顶点w和v,或者说边(v, w)和顶点v, w相关联。

图(b)所示的无向图G2可表示为
G 2 = ( V 2 , E 2 )
V 2 = { 1 , 2 , 3 , 4 }
E 2 = { ( 1 , 2 ) , ( 1 , 3 ) , ( 1 , 4 ) , ( 2 , 3 ) , ( 2 , 4 ) , ( 3 , 4 ) }
边没有方向,
(
v
,
w
)
(v,w)
(v,w)和
(
w
,
v
)
(w,v)
(w,v)表示同一条边。
3)、简单图
一个图G若满足:
①不存在重复边;
②不存在顶点到自身的边;
则称图G为简单图。
4)、多重图
若图G中某两个节点之间的边数多于一条,又允许顶点通过同一条边和自己关联,则G为多重图。
多重图的定义和简单图是相对的。
5)、完全图
对于无向图,|E|的取值范围是0到 n(n-1)/2,有n(n -1)/2条边的无向图称为完全图,在完全图中任意两个顶点之间都存在边。
对于有向图,|E|的取值范围是0 到n(n-1),有 n(n-1)条弧的有向图称为有向完全图,在有向完全图中任意两个顶点之间都存在方向相反的两条弧。上图中G2为无向完全图,而G3 为有向完全图。

每个顶点都与其他所有顶点相连。
6)、子图
设有两个图G=(V, E)和G’=(V’, E’)若V是V VV的子集,且E’ 是E的子集,则称G′是G的子图。
若有满足V(G’)= V(G)的子图G’,则称其为G的生成子图。
上图中G3为G1的子图。
注意:并非V和E的任何子集都能构成G的子图,因为这样的子集可能不是图,即E的子集中的某些边关联的顶点可能不在这个V的子集中。
7)、连通、连通图和连通分量
在无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的。若图G中任意两个顶点都是连通的,则称图G为连通图,否则称为非连通图。
无向图中的极大连通子图称为连通分量。若一个图有n个顶点,并且边数小于n-1,则此图必是非连通图。
如下图(a)所示, 图G4有3个连通分量,如图(b)所示。

注意:弄清连通、连通图、连通分量的概念非常重要。首先要区分极大连通子图和极小连通子图,极大连通子图是无向图的连通分量,极大即要求该连通子图包含其所有的边;极小连通子图是既要保持图连通又要使得边数最少的子图。
8)、强连通图、强连通分量
在有向图中,若从顶点v vv到顶点w ww和从顶点w ww到项点v vv之间都有路径,则称这两个顶点是强连通的。
若图中任何一对顶点都是强连通的,则称此图为强连通图。
有向图中的极大强连通子图称为有向图的强连通分量,图G1的强连通分量如下图所示。

注意:强连通图、强连通分量只是针对有向图而言的。一般在无向图中讨论连通性,在有向图中考虑强连通性。
9)、生成树、生成森林
连通图的生成树是包含图中全部顶点的一个极小连通子图。
若图中顶点数为n,则它的生成树含有n-1条边。
对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路。
在非连通图中,连通分量的生成树构成了非连通图的生成森林。
图G2 的一个生成树如下图所示。

注意:包含无向图中全部顶点的极小连通子图,只有生成树满足条件,因为砍去生成树的任一条边,图将不再连通。
10)、顶点的度、入度和出度
图中每个顶点的度定义为以该项点为一个端点的边的数目。
对于无向图,顶点v的度是指依附于该顶点的边的条数,记为TD(v)。
在具有n个顶点、e条边的无向图中,即无向图的全部顶点的度的和等于边数的2倍,因为每条边和两个顶点相关联。
对于有向图,顶点v的度分为入度和出度,入度是以顶点v为终点的有向边的数目,记为 ID(v); 而出度是以顶点v为起点的有向边的数目,记为OD(v)。
顶点v的度等于其入度和出度之和,即TD(v) = ID(v) + OD(v)。
在具有n个顶点、e条边的有向图中,即有向图的全部顶点的入度之和与出度之和相等,并且等于边数。这是因为每条有向边都有一个起点和终点。
11)、边的权和网
在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值。这种边上带有权值的图称为带权图,也称网。
12)、稠密图、稀疏图
边数很少的图称为稀疏图,反之称为稠密图。稀疏和稠密本身是模糊的概念,稀疏图和稠密图常常是相对而言的。一般当图G满足∣E∣<∣V∣log∣V∣时,可以将G视为稀疏图。
13)、路径、路径长度和回路
顶点vp到顶点vq之间的一条路径是指顶点序列vp,v_{i_1},v_{i_2},…,v_{i_m},v_q,当然关联的边也可以理解为路径的构成要素。路径上边的数目称为路径长度。第一个顶点和最后一个顶点相同的路径称为回路或环。若一个图有n个顶点,并且有大于n-1条边,则此图一定有环。
14)、 简单路径、简单回路
在路径序列中,顶点不重复出现的路径称为简单路径。
除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路。
15)、距离
从顶点u出发到顶点v的最短路径若存在,则此路径的长度称为从u到v的距离。若从u到v根本不存在路径,则记该距离为无穷(∞)。
16)、有向树
一个顶点的入度为0、其余顶点的入度均为1的有向图,称为有向树。
3、图的应用
1)、社交网络分析:
使用图来表示人与人之间的关系,分析社交网络的结构。
2)、路径规划:
在地图应用中使用图来表示道路网络,计算最短路径。
3)、网络拓扑:
在计算机网络中使用图来表示网络结构,优化网络性能。
4)、推荐系统:
使用图来表示用户和商品之间的关系,进行个性化推荐。
5)、生物信息学:
用图来表示蛋白质之间的相互作用网络。
6)、编译器优化:
在编译器中使用图来表示程序的控制流,进行代码优化。
7)、资源分配:
在操作系统中使用图来表示资源依赖关系,避免死锁。
二、图的存储结构
1、邻接矩阵(Adjacency Matrix)
图的邻接矩阵(Adjacency Matrix) 存储方式是用两个数组来表示图。
一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。
若有图G=(V,E),有n个顶点,则对应nxn矩阵A。

注意:无向图的邻接矩阵为对称矩阵,而有向图的邻接矩阵未必
无向图:

有向图:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // 用于INT_MAX表示无穷大
#ifndef __GRAPH_H
#define __GRAPH_H
#define MAX_VERTEX 100 // 最大顶点数
#define INF INT_MAX // 表示无穷大(无直接边)
// 图的邻接矩阵结构
typedef struct {
int vertex[MAX_VERTEX]; // 顶点信息(可存储顶点编号或其他数据)
int edge[MAX_VERTEX][MAX_VERTEX]; // 邻接矩阵:edge[i][j]表示i到j的边权
int vertexNum; // 当前顶点数
int edgeNum; // 当前边数
int isDirected; // 是否为有向图:1-有向,0-无向
} AdjMatrixGraph;
// 初始化图
void initGraph(AdjMatrixGraph *graph, int isDirected);
// 添加顶点
void addVertex(AdjMatrixGraph *graph, int data);
// 添加边(i和j为顶点索引,weight为边权)
void addEdge(AdjMatrixGraph *graph, int i, int j, int weight);
// 打印邻接矩阵
void printGraph(AdjMatrixGraph *graph);
// 深度优先搜索(DFS)递归实现
void dfs(AdjMatrixGraph *graph, int v, int *visited);
// 广度优先搜索(BFS)队列实现
void bfs(AdjMatrixGraph *graph, int start, int *visited);
// 统一调用遍历接口
void traverseGraph(AdjMatrixGraph *graph, int start, int isDFS);
// 单源最短路径(从start到所有顶点)
void dijkstra(AdjMatrixGraph *graph, int start);
// 无向图的最小生成树(从start开始)
void prim(AdjMatrixGraph *graph, int start);
// 计算每个顶点的入度(被指向的边数)
void calculateInDegree(AdjMatrixGraph *graph, int inDegree[]);
// 拓扑排序(返回1表示成功,0表示有环)
int topologicalSort(AdjMatrixGraph *graph, int topoOrder[]);
#endif
2、邻接表(Adjacency List)
邻接表,是指对图G中的每个顶点vi建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对于有向图则是以顶点vi为尾的弧),这个单链表就称为顶点vi的边表(对于有向图则称为出边表)。
边表的头指针和顶点的数据信息采用顺序存储(称为顶点表),所以在邻接表中存在两种结点:顶点表结点和边表结点,如下图所示。

无向图:

有向图:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __GRAPH_H
#define __GRAPH_H
#define MAX_VERTEX 100 // 最大顶点数
#define INF INT_MAX // 表示无穷大
// 邻接表节点(存储边信息)
typedef struct EdgeNode {
int adjVex; // 邻接顶点的索引
int weight; // 边的权值
struct EdgeNode *next; // 指向下一个邻接节点
} EdgeNode;
// 顶点节点(存储顶点信息及边表头指针)
typedef struct VertexNode {
int data; // 顶点数据
EdgeNode *firstEdge; // 边表的头指针
} VertexNode;
// 邻接表图结构
typedef struct {
VertexNode adjList[MAX_VERTEX]; // 顶点数组(邻接表)
int vertexNum; // 顶点数量
int edgeNum; // 边数量
int isDirected; // 是否为有向图:1-有向,0-无向
} AdjListGraph;
// 初始化图
void initGraph(AdjListGraph *graph, int isDirected) ;
// 添加顶点
void addVertex(AdjListGraph *graph, int data) ;
// 查找顶点数据对应的索引(辅助函数)
int findVertexIndex(AdjListGraph *graph, int data) ;
// 添加边(from和to为顶点数据,weight为权值)
void addEdge(AdjListGraph *graph, int fromData, int toData, int weight);
// 打印邻接表
void printGraph(AdjListGraph *graph) ;
// 深度优先搜索(DFS)递归实现
void dfs(AdjListGraph *graph, int v, int *visited);
// 广度优先搜索(BFS)队列实现
void bfs(AdjListGraph *graph, int start, int *visited);
// 统一调用遍历接口(startData为起始顶点数据)
void traverseGraph(AdjListGraph *graph, int startData, int isDFS) ;
// 释放图的内存(避免内存泄漏)
void freeGraph(AdjListGraph *graph) ;
//Dijkstra算法(非负权图,单源最短路径)
void dijkstra(AdjListGraph *graph, int startData);
// Bellman-Ford算法(支持负权边,单源最短路径)
// 返回1表示无负环,0表示存在负环
int bellmanFord(AdjListGraph *graph, int startData);
#endif
3、十字链表(Orthogonal List)
十字链表是有向图的一种链式存储结构。
十字链表可以解决用邻接链表储存的有向图求顶点degree的问题。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // 用于INT_MAX表示无穷大
#ifndef __GRAPH_H
#define __GRAPH_H
#define MAX_VERTEX 100 // 最大顶点数
#define INF INT_MAX // 表示无穷大(无直接边)
// 边节点结构(十字链表的边节点)
typedef struct ArcNode {
int tailVex; // 弧尾(起点)顶点索引
int headVex; // 弧头(终点)顶点索引
struct ArcNode *hlink; // 指向同弧头的下一条弧(入边表)
struct ArcNode *tlink; // 指向同弧尾的下一条弧(出边表)
int weight; // 边的权值
} ArcNode;
// 顶点节点结构
typedef struct VexNode {
int data; // 顶点数据
ArcNode *firstIn; // 入边表头指针(指向第一条以该顶点为弧头的弧)
ArcNode *firstOut; // 出边表头指针(指向第一条以该顶点为弧尾的弧)
} VexNode;
// 十字链表图结构
typedef struct {
VexNode vexs[MAX_VERTEX]; // 顶点数组
int vertexNum; // 顶点数量
int arcNum; // 边(弧)数量
} OrthoGraph;
// 初始化十字链表图
void initGraph(OrthoGraph *graph);
// 添加顶点
void addVertex(OrthoGraph *graph, int data);
// 查找顶点数据对应的索引
int findVertexIndex(OrthoGraph *graph, int data) ;
// 添加有向边(弧):fromData -> toData,权值为weight
void addArc(OrthoGraph *graph, int fromData, int toData, int weight);
// 打印十字链表(输出每个顶点的入边和出边)
void printGraph(OrthoGraph *graph);
// 深度优先搜索(DFS):从startData开始
void dfs(OrthoGraph *graph, int v, int *visited) ;
// 广度优先搜索(BFS):从startData开始
void bfs(OrthoGraph *graph, int start, int *visited);
// 统一遍历接口
void traverseGraph(OrthoGraph *graph, int startData, int isDFS) ;
// 释放十字链表内存(避免内存泄漏)
void freeGraph(OrthoGraph *graph);
// Dijkstra算法(非负权图,单源最短路径)
void dijkstra(OrthoGraph *graph, int startData);
// Bellman-Ford算法(支持负权边,检测负环)
int bellmanFord(OrthoGraph *graph, int startData);
#endif
4、邻接多重表(Adjacency Multilist)
邻接多重表是无向图的另一种链式存储结构。
用于解决用邻接表存储的无向图每条边都要存储两遍的问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // 用于INT_MAX表示无穷大
#ifndef __GRAPH_H
#define __GRAPH_H
#define MAX_VERTEX 100 // 最大顶点数
#define INF INT_MAX // 表示无穷大(无直接边)
// 边节点结构(邻接多重表的边节点)
typedef struct EdgeNode {
int mark; // 标记位(可用于遍历标记)
int ivex, jvex; // 边所依附的两个顶点的索引
struct EdgeNode *ilink; // 指向与ivex相关的下一条边
struct EdgeNode *jlink; // 指向与jvex相关的下一条边
int weight; // 边的权值
} EdgeNode;
// 顶点节点结构
typedef struct VexNode {
int data; // 顶点数据
EdgeNode *firstEdge; // 指向第一条依附于该顶点的边
} VexNode;
// 邻接多重表图结构
typedef struct {
VexNode adjmulist[MAX_VERTEX]; // 顶点数组
int vertexNum; // 顶点数量
int edgeNum; // 边数量
} MultiGraph;
// 初始化邻接多重表
void initGraph(MultiGraph *graph);
// 添加顶点
void addVertex(MultiGraph *graph, int data) ;
// 查找顶点数据对应的索引
int findVertexIndex(MultiGraph *graph, int data);
// 添加无向边(v1和v2为顶点数据,weight为权值)
void addEdge(MultiGraph *graph, int v1Data, int v2Data, int weight) ;
// 打印邻接多重表(输出每个顶点的依附边)
void printGraph(MultiGraph *graph);
// 深度优先搜索(DFS):递归实现,利用mark标记边避免重复访问
void dfs(MultiGraph *graph, int v, int *visited) ;
// 广度优先搜索(BFS):队列实现,利用mark标记边避免重复访问
void bfs(MultiGraph *graph, int start, int *visited);
// 统一遍历接口(重置边的mark标记)
void traverseGraph(MultiGraph *graph, int startData, int isDFS);
// 释放邻接多重表内存(避免内存泄漏)
void freeGraph(MultiGraph *graph);
// Dijkstra算法(单源最短路径,非负权无向图)
void dijkstra(MultiGraph *graph, int startData);
#endif
5、边集数组(Edgeset Array)
边集数组是由两个一维数组构成。一个是存储顶点的信息;另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、 终点下标(end)和权(weight)组成。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> // 用于INT_MAX表示无穷大
#ifndef __GRAPH_H
#define __GRAPH_H
#define MAX_EDGE 1000 // 最大边数
#define MAX_VERTEX 100 // 最大顶点数
#define INF INT_MAX // 表示无穷大(无直接边)
// 边集数组的边结构
typedef struct {
int u; // 起点顶点索引
int v; // 终点顶点索引
int weight; // 边的权值
} Edge;
// 边集数组图结构
typedef struct {
Edge edges[MAX_EDGE]; // 边集数组
int vertex[MAX_VERTEX];// 顶点数据数组
int vertexNum; // 顶点数量
int edgeNum; // 边数量
int isDirected; // 是否为有向图(1-有向,0-无向)
} EdgeSetGraph;
// 初始化图
void initGraph(EdgeSetGraph *graph, int isDirected);
// 添加顶点
void addVertex(EdgeSetGraph *graph, int data);
// 查找顶点数据对应的索引
int findVertexIndex(EdgeSetGraph *graph, int data);
// 添加边(uData->vData,权值weight)
void addEdge(EdgeSetGraph *graph, int uData, int vData, int weight);
// 打印边集数组
void printGraph(EdgeSetGraph *graph);
// 获取顶点v的所有邻接顶点(辅助函数)
void getAdjacentVertices(EdgeSetGraph *graph, int v, int adj[], int *adjCount) ;
// 深度优先搜索(DFS):递归实现
void dfs(EdgeSetGraph *graph, int v, int *visited) ;
// 广度优先搜索(BFS):队列实现
void bfs(EdgeSetGraph *graph, int start, int *visited);
// 统一遍历接口
void traverseGraph(EdgeSetGraph *graph, int startData, int isDFS);
// 克鲁斯卡尔(Kruskal)算法求最小生成树(无向图)
// 并查集查找(路径压缩)
int find(int parent[], int x);
// 并查集合并(按秩合并)
void unionSet(int parent[], int rank[], int x, int y) ;
// 边的比较函数(用于排序)
int compareEdges(const void *a, const void *b);
// 克鲁斯卡尔算法求最小生成树
void kruskal(EdgeSetGraph *graph);
#endif
三、图的遍历
1、深度优先遍历
1)、DFS算法
深度优先搜索类似于树的先序遍历。
如其名称中所暗含的意思一样,这种搜索算法所遵循的搜索策略是尽可能“深”地搜索一个图。
它的基本思想如下:首先访问图中某一起始顶点v,然后由v出发,访问与v邻接且未被访问的任一顶点w1,再访问与w1邻接且未被访问的任一顶点…重复上述过程。
当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。
例:

其深度优先遍历的结果为a b d e h c f g
2)、DFS算法的性能分析
DFS算法是一个递归算法,需要借助一个递归工作栈,故其空间复杂度为O(V)。
对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O(V2)的时间。而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O(V+E)。
显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。
对于有向图而言,由于它只是对通道存在可行或不可行,算法上没有变化,是完全可以通用的。
3)、深度优先的生成树和生成森林
深度优先搜索会产生一棵深度优先生成树。 当然,这是有条件的,即对连通图调用DFS才能产生深度优先生成树,否则产生的将是深度优先生成森林,如下图所示。
基于邻接表存储的深度优先生成树是不唯一的 。

2、广度优先遍历
1、BFS算法
如果说图的深度优先遍历类似树的前序遍历,那么图的广度优先遍历就类似于树的层序遍历了。
广度优先搜索是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的算法。
为了实现逐层的访问,算法必须借助一个辅助队列,以记忆正在访问的顶点的下一层顶点。
例:

其广度优先遍历的结果为a b c d e f g h。
2、BFS算法性能分析
无论是邻接表还是邻接矩阵的存储方式,BFS 算法都需要借助一个辅助队列Q, n个顶点均需入队一次,在最坏的情况下,空间复杂度为O(V)。
采用邻接表存储方式时,每个顶点均需搜索一次(或入队一次), 在搜索任一顶点的邻接点时,每条边至少访问一次,算法总的时间复杂度为O(V+E)。采用邻接矩阵存储方式时,查找每个顶点的邻接点所需的时间为O(V),故算法总的时间复杂度为O(V2)。
四、最小生成树
一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n − 1 n-1n−1条边,若砍去它的一条边,则会使生成树变成非连通图;若给它增加一条边,则会形成图中的一条回路。
1、普里姆(Prim)算法
Prim算法构造最小生成树的过程如下图所示。
初始时从图中任取一顶点(如顶点加入树T,此时树中只含有一个顶点,之后选择一个与当前T中顶点集合距离最近的顶点,并将该顶点和相应的边加入T,每次操作后T中的顶点数和边数都增1。
以此类推,直至图中所有的顶点都并入T,得到的T就是最小生成树。此时T中必然有n-1条边。
通俗点说就是:从一个顶点出发,在保证不形成回路的前提下,每找到并添加一条最短的边,就把当前形成的连通分量当做一个整体或者一个点看待,然后重复“找最短的边并添加”的操作。


2、克鲁斯卡尔(Kruskal)算法
与Prim算法从顶点开始扩展最小生成树不同,Kruskal 算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。
Kruskal算法构造最小生成树的过程如下图所示。
初始时为只有n个顶点而无边的非连通图T = V , T= {V, {}}T=V,,每个顶点自成一个连通分量,然后按照边的权值由小到大的顺序,不断选取当前未被选取过且权值最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入T TT,否则舍弃此边而选择下一条权值最小的边。

算法思路:
我们可以直接就以边为目标去构建,因为权值是在边上,直接去找最小权值的边来构建生成树也是很自然的想法,只不过构建时要考虑是否会形成环路而已。此时我们就用到了图的存储结构中的边集数组结构。

五、最短路径
在网图和非网图中,最短路径的含义是不同的。
由于非网图它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。
1、迪杰斯特拉(Dijkstra)算法
Dijkstra算法用于构建单源点的最短路径—,即图中某个点到任何其他点的距离都是最短的。例如,构建地图应用时查找自己的坐标离某个地标的最短距离。可以用于有向图,但是不能存在负权值。

迪杰斯特拉(Dijkstra) 算法,它并不是一下子求出了v0到v8的最短路径,而是一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到你要的结果。
Dijkstra算法设置一个集合S记录已求得的最短路径的顶点。
在构造的过程中还设置了个辅助数组:
dist[]:记录从源点v 0到其他各顶点当前的最短路径长度,它的初态为:若从v0到vi;
有弧,则dist[i]为弧上的权值;否则置dist[i]为∞。

2、弗洛伊德(Floyd)算法

算法执行过程的说明如下:

用Floyd算法求所有顶点之间的最短路径长度的过程如下表所示:

从这个表中,可以发下一些规律:

六、拓扑排序
1、定义
AOC网:
用一个有向图表示一个工程的各个子工程及其相互制约关系。
其中顶点表示活动,弧表示优先制约关系。
AOE网:
弧表示活动,以顶点表示活动开始或结束事件。
拓扑排序:
在AOV网没有回路的前提下,将全部活动排列成一个线性序列。
AOV网中边(i,j)存在,则在这个序列中i一定排在j的前面。
对AOV网进行如上排序,即为拓扑排序。
2、算法
对一个AOV网进行拓扑排序的算法有很多,下面介绍比较常用的一种方法的步骤:
①从AOV网中选择一个没有前驱的顶点并输出。
②从网中删除该顶点和所有以它为起点的有向边。
③重复①和②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。如果输出顶点数少了,哪怕是少了一个,也说明这个网存在环(回路),不是AOV网。

七、关键路径
1、定义
在AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),它表示整个工程的开始;网中也仅存在一个出度为0的顶点,称为结束顶点(汇点),它表示整个工程的结束。
我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。

2、算法

重要参数:

求关键路径的算法步骤如下:


4901

被折叠的 条评论
为什么被折叠?



