1. 定义、概念
1.1 graph
简单图G=(V,E) :有穷非空顶点集合V、可空边E
1.2 各种图定义
(1)无向边:顶点a到b的边没有方向,则边为无向边edge (a,b) 同(b,a)
无向图:图中任意两个顶点之间的边都是无向边 undirected graphs
上面的无向图G=(V,{E}) V={A,B,C,D} E={(A,B),(B,C),(C,D),(D,A),(A,C)}
(2)有向边:顶点a到b的边有方向。有向边也叫弧arc <a,b> a是弧头,b是弧尾 不能写成<b,a>
有向图:图中任意两个顶点之间的边都是有向边 directed graphs
上面的有向图:G=(V,{E}) V={A,B,C,D} E={<A,D>,<B,A>,<C,A>,<B,C>}
(3)简单图
在图中不存在顶点到自身的边,且同一条边不重复出现
上面的两个图就不是简单图
(4)完全图
①无向完全图
任意两个顶点之间都存在边
n个顶点的无向完全图有:(n-1)+(n-2)+...+1=n*(n-1)/2 条边
②有向完全图
任意两个顶点之间存在方向互为相反的两条弧
n个顶点的有向完全图有:n*(n-1) 条边
(5)稀疏图、稠密图
边或弧相对较少的是稀疏图,相对较多的是稠密图
(6)网
边或弧上可能有权值weight
带权的图叫做 网 network
(7)子图 subgraph
图的一部分顶点和一部分边或弧组成子图
1.3 图的顶点与边的关系
(1)相邻、度
①无向图
若G=(V,{E}),边(a,b)属于E ,则顶点a、b互为邻接点,边(a,b)依附与顶点a和b(边与a、b相关联)
顶点a的度degree:和这个顶点相关联的边的数目,记为 TD(a)
无向图边的个数是各顶点度数和的一半
②有向图
若G-(V,{E}),弧<a,b>属于E,则顶点a邻接到顶点b,而顶点b邻接自顶点a,弧<a,b>与顶点a、b相关联
顶点a的入度indegree:以顶点a为终点的弧(弧头)的数目 记为ID(a)
顶点a的出度outdegree:以顶点a为起始的弧(弧尾)的数目 记为OD(a)
顶点a的度:TD(a)=ID(a)+OD(a)
(2)路径
无向图:从顶点a到顶点b的路径path是一个顶点序列
有向图:路径也是有向的,顶点序列要满足原本的方向关系
路径长度:路径上的边或弧的数目
如果路径第一个顶点和最后一个顶点相同,则路径为回路或环cycle。
序列中顶点不重复出现的路径叫简单路径。
除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路叫简单回路或简单环
1.4 连通图
(1)无向图
无向图G中,如果从顶点a到顶点b有路径,则a和b是连通的,如果图中任意两个顶点都是连通的, 则G是连通图 connected graph
上图不是连通图,因为从A到E没有路径
无向图的极大连通子图叫连通分量:①子图②子图连通③连通子图有极大顶点数④有极大顶点数的连通子图包含依附于这些顶点的所有边
上面图1的极大连通图有:如下
(2)有向图
在有向图G中,如果对每一对不同的顶点,如a、b,从a到b和从b到a都存在路径,则图G是强连通图
有向图的极大强连通子图叫有向图的强连通分量
这是一个强连通图,可能它本身是另一个非强连通图的最大强连通子图,则这个是强连通分量
1.5 连通图的生成树
(1)
一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边
图1就不是生成树 图2是生成树 图3也是生成树
生成树:则一定有n个顶点和n-1条边 反之不一定
若图有n个顶点:边数少于n-1,则图不连通
边数多于n-1,则必有环
(2)有向图
若一个有向图恰好有一个顶点入度为0(是根结点),其余顶点入度均为1(该顶点只有一个双亲),则是一棵有向树
一个有向图的生成森林由若干有向树组成,含图中全部顶点,但只有足以构成若干棵不相交的有向树的弧
一个普通的有向图,去掉一些弧分解成两个有向树如下
B的入度为0,其余顶点入度为1 F入度为0,其余顶点入度为1
2. 存储结构
2.1 邻接矩阵 adjacency matrix
用两个结构分别存储顶点和边或弧。顶点不分大小主次,可用一维数组存储;边或弧用二维数组存储
设图G有n个顶点,则邻接矩阵是一个n*n方阵(对称矩阵),定义如下
(1)一个无向图及其邻接矩阵如下:
根据矩阵可获得信息:
判断任意两顶点是否有边
顶点度:该顶点所在行或列的元素之和
顶点的邻接点:扫描该顶点所在行,arc[i][j]为1就是邻接点
(2)有向图:
上图,v1到v0有边,则arc[1][0]为1, 从v0到v1没有,则arc[0][1]为0
(3)网:边带权值的图
∞表示一个计算机允许的、大于所有边上权值的值
一个网及其邻接矩阵如下:
(4)邻接矩阵基本实现
1 typedef char VertexType; //用户自定义顶点类型 2 typedef int EdgeType; //边上权值由用户自定义 3 #define MAXVEX 100 //最大顶点数,用户定义 4 #define INFINITY 65535 //无穷 5 typedef struct { 6 VertexType vexs[MAXVEX]; //顶点表 7 EdgeType arc[MAXVEX][MAXVEX]; //邻接矩阵,可看作边表 8 int numVertexes,numEdges; //图中当前的顶点数和边数 9 }MGraph;
创建邻接矩阵:
1 void createMGraph(MGraph *G) 2 { 3 int i,j,k,w; 4 cin>>G->numVertexes>>G->numEdges; //输入顶点数和边数 5 //建立顶点表 6 for(i=0;i<G->numVertexes;i++) { 7 cin>>G->vexs[i]; 8 } 9 //邻接矩阵初始化 10 for(i=0;i<G->numVertexes;i++) { 11 for(j=0;j<G->numVertexes;j++) { 12 G->arc[i][j] = INFINITY; 13 } 14 } 15 //读入 numEdges 条边,建立邻接矩阵 16 for(k=0;k<G->numEdges;k++) { 17 cin>>i>>j>>w; //输入边(vi,vj)的上表i,下标j,权重w 18 G->arc[i][j] = w; 19 G->arc[j][i] = w; //无向图,矩阵对称 20 } 21 }
假设n个顶点,e条边,则邻接矩阵的创建复杂度 O(n+n2+e) 需要空间O(n2)
如果图的边数相对顶点数较少,则会浪费很多存储空间
2.2 邻接表 adjacency list
顶点用一个一维数组或单链表存储。每个数据元素还需要存储指向第一个邻接点的指针以便于寻找该顶点的边信息。
每个顶点的所有邻接点构成一个线性表,用单链表存储。无向图称为顶点a的边表,有向图称为顶点a作为弧尾的出边表。
(1)一个无向图的邻接表
(2)有向图
以顶点为弧尾(弧的起始),便于计算出度 或以顶点作为弧头(弧的终结),便于计算入度
(3)带权值的网
在边表结点中增加 weight 数据域
(4)简单实现
1 typedef char VertexType; //用户自定义顶点类型 2 typedef int EdgeType; //边上权值由用户自定义 3 #define MAXVEX 100 4 5 typedef struct EdgeNode { //边表结点 6 int adjvex; //邻接点域,存储该顶点对应的下标 7 EdgeType weight; //权值 8 struct EdgeNode *next; //链域,指向下一个邻接点 9 }EdgeNode; 10 11 typedef struct VertexNode { //顶点表结点 12 VertexType data; //顶点信息 13 EdgeNode * firstEdge; //边表头指针 14 }VertexNode,AdjList[MAXVEX]; 15 16 typedef struct { //邻接表 17 AdjList adjList; //所有顶点 18 int numVertexes,numEdges; //顶点数、边数 19 }GraphAdjList;
1 void creatALGraph(GraphAdjList *G) 2 { 3 int i,j,k; 4 EdgeNode* e = nullptr; 5 cout<<"输入顶点数和边数:"; 6 cin>>G->numVertexes>>G->numEdges; //输入顶点数和边数 7 for(i=0;i<G->numVertexes;i++) { //输入顶点信息 8 cin>>G->adjList[i].data; 9 G->adjList[i].firstEdge = nullptr; 10 } 11 for(k=0;k<G->numEdges;k++) { //建立边表 12 cout<<"输入边(vi,vj)上的顶点序号:"; 13 cin>>i>>j; 14 e = (EdgeNode*)malloc(sizeof(EdgeNode)); //申请一个边结点空间 15 e->adjvex = j; //以vi为弧的头,即弧的开始 16 e->weight = 1; //权值默认为1 17 e->next = G->adjList[i].firstEdge; 18 G->adjList[i].firstEdge = e; //上面两行,将新的被指向结点j放到i的指向的第一条边 19 //对于有向图,上面的就够了 因为是vi指向vj 20 //对于无向图,还需要下面的,j的边表里面还要有i结点 21 e = (EdgeNode*)malloc(sizeof(EdgeNode)); 22 e->adjvex = i; 23 e->weight = 1; 24 e->next = G->adjList[j].firstEdge; 25 G->adjList[j].firstEdge = e; 26 } 27 }
时间复杂度:n个顶点,e条边则为O(n+e) 空间复杂度:如果顶点表用动态数组存储则只需要顶点数目的空间和边数目的空间
2.3 十字链表 Orthogonal list
对于有向图的邻接表逆邻接表,只能很方便的计算某个顶点的出度,要计算入度则要遍历整个表
十字链表结合邻接表和逆邻接表
原来的顶点结点只有一个链接域,改成两个链接域:firstin指向入边表头指针 firstout指向出边表头指针
边表结点如下:
tailvex:弧起点(弧尾)在顶点表的下标 headvex:弧终点(弧头)在顶点表的下标
headlink:入边表链接域,指向终点相同的下一条边 taillink:出边表链接域,指向起点相同的下一条边
一个有向图及其十字链表:
简单实现:C++
1 class Vertex { 2 public: 3 friend class Edge; 4 Vertex(int x=0,void* p=nullptr); 5 Edge* getFirstOutEdge() const; 6 Edge* getFirstInEdge() const; 7 void linkTo(Vertex*); 8 bool linkedWith(Vertex*); 9 void deleteVertex(); 10 void setId(int); 11 void* getData(); 12 ~Vertex(); 13 private: 14 Edge * firstOutEdge; //指向以当前结点为起点(弧尾)的边 15 Edge * firstInEdge; //指向以当前结点为终点(弧头)的边 16 int id; 17 void* data; 18 }; 19 20 class Edge{ 21 public: 22 friend class Vertex; 23 Edge(); 24 Edge(Vertex* s,Vertex* d); 25 void setSrc_Dst(Vertex* s,Vertex *d); 26 void deleteEdge(); 27 Edge* getNextEdge_src() const; 28 Edge* getNextEdge_dsr() const; 29 Vertex* getSrc() const; 30 Vertex* getDst() const; 31 ~Edge(); 32 private: 33 Vertex* src; //指向边的起点,弧尾 34 Vertex* dst; //指向边的终点,弧头,箭头 35 Edge* nextEdge_src; //指向相同起点的下一条边 36 Edge* nextEdge_dst; //指向相同终点的下一条边 37 };
具体实现:....再说吧 吐了
2.4 邻接多重表
对于无向图,重点在顶点则可用邻接表,重点在边上(对访问过的边做操作、删除边等 )用邻接多重表
上面无向图的邻接表如下:需要10个边结点
邻接多重表:
边结点:
ivex:边依附的顶点在顶点表的下标 jvex:同
ilink:依附顶点ivex的下一条边
jlink:依附jvex的下一条边
如果要删除(v1,v4),则只要删除该边结点,以及修改两个指针(指向(v1,v4)的ilink、指向(v1,v4)的jlink)
2.5 边集数组
两个一维数组,一个存储顶点信息,一个存储边信息(数据元素由起点下标begin、终点下标end、权组成)
边集数组关注边,要对顶点操作,如计算度则需要遍历整个边数组
3. 遍历图
2.1 深度优先遍历 depth first search
邻接矩阵的深度优先遍历:
1 void DFSTraverse(MGraph G) 2 { 3 int i = 0; 4 bool* visited = (bool*)malloc(G.numVertexes*sizeof(bool)); 5 for(i=0;i<G.numVertexes;++i) { 6 visited[i] = false; 7 } 8 for(i=0;i<G.numVertexes;++i) { 9 if(!visited[i]) { 10 DFS(G,i,visited); 11 } 12 } 13 free(visited); 14 } 15 16 void DFS(MGraph G,int i,bool* visited) 17 { 18 int j = 0; 19 visited[i] = true; 20 cout<<G.vexs[i]; //对顶点进行操作 21 for(j=0;j<G.numVertexes;++j) { 22 if(G.arc[i][j] == 1 && !visited[j]) { 23 DFS(G,j,visited); 24 } 25 } 26 }
邻接表的深度优先遍历:
1 static void DFS(GraphAdjList GL,int i,bool* visited) 2 { 3 EdgeNode* p = nullptr; 4 visited[i] = true; 5 cout<<GL.adjList[i].data; //对顶点进行操作 6 p = GL.adjList[i].firstEdge; 7 while(p) { 8 if(!visited[p->adjvex]) { 9 DFS(GL,p->adjvex,visited); 10 } 11 p = p->next; 12 } 13 } 14 void DFSTraverse(GraphAdjList GL) 15 { 16 int i = 0; 17 bool* visited = (bool*)malloc(GL.numVertexes*sizeof(bool)); 18 for(i=0;i<GL.numVertexes;++i) { 19 visited[i] = false; 20 } 21 for(i=0;i<GL.numVertexes;++i) { 22 if(!visited[i]) { 23 DFS(GL,i,visited); 24 } 25 } 26 free(visited); 27 }
2.2 广度优先遍历
void BFSTraverse(MGraph &G) { int i,j; queue<int> q; vector<bool> visited(G.numVertexes,false); for(i=0;i<G.numVertexes;i++) { if(!visited[i]) { cout<<G.vexs[i]<<" "; q.push(i); while(!q.empty()) { int k = q.front(); q.pop(); for(j=0;j<G.numVertexes;j++) { if(G.arc[k][j]==1 && !visited[j]) { visited[j] = true; cout<<G.vexs[j]<<" "; q.push(j); } } } } } }
4. 最短路径
网图两顶点之间经过的边上权值之和最小的路径
4.1 Dijkstra算法
按路径长度递增的次序产生最短路径。采用 贪心算法 思想,对有向/无向带权图寻找最短路径。
对不含负权的网,是最快的 单源最短路径算法 O(n2)
步骤:
由于是单源路径,我以vo为起始点
用一个数组distance存储从v0到其它各点的最短路径值,用数组parent存储从v0到vi中间要经过的点的下标(规定v0在顶点表下标为0,vi的下标为i)
先将v0到各点的直线距离存储进distance(没有直接连线则距离为无穷值 这里用65535表示INFINITY)
首先,找到v0到其它点的直线距离中最短的路径(第一次执行行15到行24),记下这个点是vj。后面循环再次执行着几行,就不是找v0到其它点的最短直线距离,而是最短距离
行25到行30是关键,对未访问过的点(used里面没标记为1的),如果v0到这个点vn的直线距离(第一次循环里面是直线距离)比从v0到vj加上从vj到vn的和还要大,则从v0到vn的最短路径就是先从v0到vj,再从vj到vn,而不是直接从v0到vn了,这时更新一下distance[n],并且从v0到vn中间经过vj,所以在parent[n]里存下这个中间点下标j。
后面的循环:行14的第二次及以后的循环
也是先找v0到其它点的最短距离,这里就不是直线距离了。注意行18的条件used[m]==0
1 void dijkstra(MGraph &G) 2 { 3 int i,j,k,m,n; 4 int mini; 5 int* used = (int*)malloc(G.numVertexes*sizeof(int)); 6 int* distance=(int*)malloc(G.numVertexes*sizeof(int)); //存储起始顶点到各点的最短路径 7 int* parent=(int*)malloc(G.numVertexes*sizeof(int)); 8 for(i=0;i<G.numVertexes;i++) { 9 used[i] = 0; 10 parent[i] = 0; 11 distance[i] = G.arc[0][i]; //矩阵第0行的数据 12 } 13 used[0] = 1; 14 for(i=0;i<G.numVertexes-1;i++) { 15 j=0; 16 mini = INFINITY; 17 for(m=0;m<G.numVertexes;m++) { 18 if(used[m]==0 && distance[m]<mini) { 19 mini = distance[m]; 20 j=m; 21 } 22 } 23 //mini为行0的最小值,j为顶点下标 (vo,vj)==mini 24 used[j] = 1; 25 for(n=0;n<G.numVertexes;n++) { 26 if(used[n]==0 && (distance[n]>distance[j]+G.arc[j][n])) { 27 distance[n] = distance[j]+G.arc[j][n]; 28 parent[n] = j; 29 } 30 } 31 32 //输出每次循环后distance里面的内容 33 // for(k=0;k<G.numVertexes;++k) { 34 // cout<<distance[k]<<" "; 35 // } 36 // cout<<"\n"; 37 } 38 //最短路径算法执行完毕,distance里面存储从第一个结点v0到其它各结点的最短路径值 39 //parent里面存储路线 40 for(k=0;k<G.numVertexes;++k) { 41 cout<<distance[k]<<" "; 42 } 43 cout<<"\n"; 44 for(k=0;k<G.numVertexes;++k) { 45 cout<<parent[k]<<" "; 46 } 47 cout<<"\n"; 48 free(used); 49 free(parent); 50 free(distance); 51 }
画一个简单的网:
需要3次进到行14的循环里面去
distance=[x,20,5,100]
第一次:
行15到24:找到最短的路径值是从v0到v2 mini=5 j=2 used[2]=1
行25到30:
v0到v1:v0到v1距离为20 > (v0,v2)+(v2,v1) 不成立
v0到v3:v0到v3距离100 > (v0,v2)+(v2,v3) ,100>5+40 成立,所以distance[3]==45 parent[3]=2
distance=[x,20,5,45]
第二次:
行15到24:找到最短的是从v0到v1(v0到v2的这条已经计算过了,used[2]被标记了) mini=20 j=1 used[1]=1
行25到30:
v0到v3:45>(v0,v1)+(v1,v3) ,即45>20+1 成立,说明从v0到v3中间经过v1比中间经过v2距离要短些,更新distance[3]=21 parent[3]=1
distance=[x,20,5,21]
第三次:
used[0]、used[1]、used[2]都被标记了,只剩下v3
mini=21 j=3 used[3]=1
(v0,v3)>(v0,v3)+(v3,v3)不成立
distance=[x,20,5,21]
parent=[0,0,0,1]
完成最短路径计算
dijkstra算法只适用计算单源最短路径,即从v0到其它任意点的最短路径,复杂度为O(n2)。如果要计算任意两点间最短距离,则要把每个顶点当作源点运行一次这个算法,复杂度变成了O(n3)。
4.2 Floyd算法
利用动态规划的思想
可处理带负权的多源最短路径问题,不能处理有负权回路的图。
时间复杂度为O(n3) 空间复杂度为O(n2)
1 void floyd(MGraph &G) 2 { 3 int **dis = getMNMatrix<int>(G.numVertexes,G.numVertexes); 4 int **pa = getMNMatrix<int>(G.numVertexes,G.numVertexes); 5 int v,w,k; 6 //初始化两个矩阵 distance为邻接矩阵,path[i][j]==j 7 for(v=0;v<G.numVertexes;++v) { 8 for(w=0;w<G.numVertexes;++w) { 9 dis[v][w] = G.arc[v][w]; 10 pa[v][w] = w; 11 } 12 } 13 for(k=0;k<G.numVertexes;++k) { 14 for(v=0;v<G.numVertexes;++v) { 15 for(w=0;w<G.numVertexes;++w) { 16 if(dis[v][w] > dis[v][k]+dis[k][w]) { 17 dis[v][w] = dis[v][k]+dis[k][w]; 18 pa[v][w] = pa[v][k]; 19 } 20 } 21 } 22 } 23 print_matrix<int>(dis,G.numVertexes,G.numVertexes); 24 print_matrix<int>(pa,G.numVertexes,G.numVertexes); 25 //输出各点到其它点的最短路径以及中间经过的点 26 printPath(pa,dis,G); 27 freeMatrix(dis,G.numVertexes,G.numVertexes); 28 freeMatrix(pa,G.numVertexes,G.numVertexes); 29 }
行13到22为floyd算法的关键部分
其它调用的函数:
打印路径详细信息:
1 static void printIJPath(int **pa,MGraph &G,int i,int j) 2 { 3 cout<<G.vexs[i]<<" "; 4 while(i != j) { 5 cout<<G.vexs[pa[i][j]]<<" "; 6 i = pa[i][j]; 7 } 8 } 9 static void printPath(int **pa,int **dis,MGraph &G) 10 { 11 for(int i=0;i<G.numVertexes;++i) { 12 for(int j=0;j<G.numVertexes;++j) { 13 if(i==j) { 14 continue; 15 } 16 cout<<"从"<<G.vexs[i]<<"到"<<G.vexs[j]<<"的路径为:"; 17 printIJPath(pa,G,i,j); 18 cout<<"长度为:"<<dis[i][j]<<endl; 19 } 20 } 21 }
获取m*n矩阵:
1 template <typename T> 2 T** getMNMatrix(int m,int n) 3 { 4 if(m<1 || n<1) { 5 return nullptr; 6 } 7 T** pary = nullptr; 8 pary = (T**)malloc(m*sizeof(T*)); 9 if(pary == nullptr) { 10 return nullptr; 11 } 12 for(int i=0;i<m;++i) { 13 pary[i] = (T*)malloc(n*sizeof(T)); 14 if(pary[i] == nullptr) { 15 freePartMatrix(pary,i); 16 pary = nullptr; 17 return nullptr; 18 } 19 memset(pary[i],0,n*sizeof(T)); 20 } 21 return pary; 22 }
1 template <typename T> 2 /* 3 功能:malloc申请空间部分成功,部分失败,则回收成功的部分 4 */ 5 static void freePartMatrix(T** pary,int i) 6 { 7 for(int j=i-1;j>=0;j--) { 8 if(pary[j] != nullptr) { 9 free(pary[j]); 10 pary[j] = nullptr; 11 } 12 } 13 free(pary); 14 }
1 /* 2 功能:回收整个二维数组 3 */ 4 template <typename T> 5 void freeMatrix(T** arr,int m,int n) 6 { 7 if(arr == nullptr) { 8 return; 9 } 10 for(int i=m-1;i>=0;--i) { 11 if(arr[i] != nullptr) { 12 free(arr[i]); 13 arr[i] = nullptr; 14 } 15 } 16 if(arr != nullptr) { 17 free(arr); 18 } 19 }
1 template <typename T> 2 void print_matrix(T** ary,int m,int n) 3 { 4 if(ary == nullptr) { 5 cout<<"Empty Matrix"<<endl; 6 } 7 for(int i=0;i<m;++i){ 8 for(int j=0;j<n;++j) { 9 cout<<ary[i][j]<<" "; 10 } 11 cout<<"\n"; 12 } 13 }
对4.1的图调用floyd算法:
最短路径值矩阵dis、路径矩阵pa如下:
打印路径信息:
5. 最小生成树
一个连通图的生成树:一个极小连通子图,含图中全部顶点,但只有足以构成一棵树的n-1条边
构造连通网的最小代价生成树是最小生成树
一个简单无向网及其邻接矩阵:
5.1 prim算法
以v0为起点
1 void miniSpanTree_prim(MGraph &G) 2 { 3 int min,i,j,k; 4 int adjvex[MAXVEX]; //保存相关顶点下标 5 int lowcost[MAXVEX]; //保存相关顶点间边的权值 6 lowcost[0] = 0; //初始化第一个权值为0,即v0加入生成树 7 adjvex[0] = 0; //初始化第一个顶点下标为0 8 for(i=1;i<G.numVertexes;++i) { //读入矩阵第一行的数据 9 lowcost[i] = G.arc[0][i]; //将与v0顶点有关的边的权存入数组 10 adjvex[i] = 0; 11 } 12 for(i=0;i<G.numVertexes;++i) { 13 min = INFINITY; //初始化最小权值为无穷 14 j = 1; k = 0; 15 while(j<G.numVertexes) { 16 if(lowcost[j]!=0 && lowcost[j]<min) { 17 min = lowcost[j]; 18 k = j; 19 } 20 ++j; 21 } 22 cout<<adjvex[k]<<" "<<k; //输出当前顶点边中权值最小的边,即(v0,vk)==min==lowcost[k] 23 lowcost[k] = 0; //当前顶点的权值设为0,表示此顶点已经完成任务 24 for(j=1;j<G.numVertexes;++j) { 25 if(lowcost[j]!=0 && G.arc[k][j]<lowcost[j]) { 26 lowcost[j] = G.arc[k][j]; 27 adjvex[j] = k; 28 } 29 } 30 } 31 }
这个和dijsktra算法很像,只不过行25,不是用距离和比较,因为最小生成树不需要计算从一个顶点到另一个顶点的距离,只要距离最小就可以了