目录
一、文章意义:
此篇文章能够帮助笔者和作者更好理解最小生成树和最短路径的知识点
二、两者的区别:
最小生成树MST:生成一棵包含全部顶点的树,并且树的边权值总和最小,同时考虑所有顶点
最短路径:找出顶点1到顶点2的最短距离,使得这一条路径上的边权值总和最小,只需要考虑顶点1和顶点2两个顶点
三、具体实现方法:
(1) MST最小生成树:
Prim's 算法
理论解释:
step 1:从联通网络N={V,E}(V表示顶点vertice,E表示边edge)中选择一点v作为出发点,将v从V中移入MST的顶点集合U中
step 2:从V中选择与v关联的权值最小的边(v,u0),将u0从V中移入MST的顶点集合U中
step 3:从V中选择与u0关联的权值最小的边(u0,u1),将u1从V中移入MST的顶点集合U中,反复执行知道所有顶点都移入U中
时间复杂度:o( Verticenum^2)=o(n^2)
适用场景:无权值、权值为负或正,有向和无向
实现的c语言代码:
(方法1)
//Prim 构造最小生成树
#define maxValue 32767//注意这个值怎么来的:是根据计算机内存量取的
#define maxVertices 10
typedef struct
{
int v1,v2,key;
}MSTEdgeNode;
typedef struct
{
MSTEdgeNode edgeValue[maxVertices];
int n;
}MinSpanTree;
typedef struct
{
int Edge[maxVertices][maxVertices];
int numVertices;
int numedge;
}MGraph;
void PrimMST(MGraph *G, MinSpanTree *T, int v)
{
MSTEdgeNode e;
int i, j, k=0, minpos, u;
int minVal, n=G->numVertices;
int visited[maxVertices];
for(i=0;i<n;i++)
{
visited[i]=0;
}
for(i=0;i<n;i++)
{
if(G->Edge[v][i]&&G->Edge[v][i]<maxValue)
{
T->edgeValue[k].v1=v;
T->edgeValue[k].v2=i;
T->edgeValue[k].key=G->Edge[v][i];
}
}
T->n=k;
for(i=0;i<n;i++)
{
minVal=T->edgeValue[i].key;
minpos=i;
for(j=i+1;j<n;j++)
if(T->edgeValue[j].key<minVal)
{
minVal=T->edgeValue[j].key;
minpos=j;
}
visited[T->edgeValue[minpos].v1]=1;
u=T->edgeValue[minpos].v2;
for(j=0;j<n;j++)
{
if(G->Edge[u][j]&&G->Edge[u][j]<maxValue&&!visited[j])
{
T->edgeValue[k].v1=u;
T->edgeValue[k].v2=j;
T->edgeValue[k].key=G->Edge[u][j];
T->n++;
}
}
if(i!=minpos)
{
e=T->edgeValue[i];
T->edgeValue[i]=T->edgeValue[minpos];
T->edgeValue[minpos]=e;
}
}
T->n=n-1;
}
int main()
{
MGraph G;
MinSpanTree T;
int v;
//具体的输入需要自己根据题目写
PrimMST(&G, &T, v);
return 0;
}
(方法2)
#define maxVertice 100
#define maxWeight 32767
typedef int Weight;
typedef struct
{
Weight Adj[maxVertice][maxVertice];
int numVertice, numEdge;
}MGraph;
void CreateG(MGraph *G)
{
scanf("%d %d",&G->numVertice,&G->numEdge);
for(int i=0;i<G->numVertice;i++)
{
for(int j=i;j<G->numVertice;j++)
{
scanf("%d",&G->Adj[i][j]);
G->Adj[j][i]=G->Adj[i][j];
}
}
}
void PrimMST(MGraph *G,int v)
{
int i,j,n=G->numVertice,minweight,minpos;
int visited[maxVertice];
int lowweight[maxVertice];
for(i=0;i<n;i++)
{
visited[i]=0;
lowweight[i]=G->Adj[v][i];
}
visited[v]=1;
for(i=0;i<n;i++)
{
minweight=maxWeight,minpos=v;
for(j=0;j<n;i++)
{
if(!visited[j]&&lowweight[j]<minweight)
{
minweight=lowweight[j];
minpos=j;
}
}
visited[minpos]=1;
for(j=0;j<n;j++)
{
if(!visited[j]&&G->Adj[minpos][j]&&G->Adj[minpos][j]<lowweight[j])
{
lowweight[j]=G->Adj[minpos][j];
}
}
}
}
int main()
{
MGraph G;
CreateG(&G);
//其余的根据题目自己添加
PrimMST(&G,0);//0代表起点
return 0;
}
Kruskal's 算法
理论解释:
step 1:连通网络N={V,E}通常以邻接矩阵或邻接表形式生成,将N中每一条边按照权值从小到大进行快速排序,记为集合H
step 2:从H中取出权值最小的边e0,加入MST中,将e0从H中移除
step 3:从H中取出权值最小的边e1,将e1从H中移除,检查e1与e0是否构成连通关系,不构成则将其加入MST,否则不加入
step 4:重复step 3,知道MST中边的数量为verticenum-1=n-1
时间复杂度:o(Edgenum log Verticenum)=o(E logV)
适用情形:
实现的c语言代码:
#define maxValue 32767
#define maxVertices 10
typedef struct
{
int v1, v2, key;
int verticenum,edgenum;
}MGraph;
typedef struct
{
}MSTEdgeNode;
typedef struct
{
MGraph edgeValue[maxValue];
int n;
}MinSpanTree;
typedef struct//双亲指针数组,并查集类型定义
{
int parent[maxVertices];
}UFSets;
void Initial(UFSets Uset)//初始化并查集
{
for(int i=0;i<maxVertices;i++) Uset.parent[i]=-1;
}
int Find(UFSets Uset,int v)//查找并返回v的根
{
while(Uset.parent[v]>=0) v=Uset.parent[v];
return v;
}
void Merge(UFSets USet, int v1,int v2)
{
if(v1==v2) return ;
//v1和v2不同时,v2成为v1的子女,v1成为合并后的树根
USet.parent[v1]=USet.parent[v1]+USet.parent[v2];
USet.parent[v2]=v1;
}
void Kruskal_MST(MGraph EV[maxVertices],int n,int e,MinSpanTree *T)
{
int k,u,v,count;
UFSets Uset;
Initial(Uset);
T->n=0,k=0,count=0;//初始化MST
while(count<n-1)
{
u=Find(Uset, EV[k].v1 );
v=Find(Uset, EV[k].v2 );
if(u!=v)
{
T->edgeValue[T->n++]=EV[k];
Merge(Uset,u,v);
count++;
}
k++;
}
if(count>=n) printf("不连通的图,无法生存MST\n");
else
{
printf("MST:\n");
for(int i=0;i<T->n;i++) printf("(%d,%d,%d)",T->edgeValue[i].v1,T->edgeValue[i].v2,T->edgeValue[i].key);
printf("\n");
}
}
int main()
{
MGraph EV[maxVertices];//构造邻接矩阵EV[]
MinSpanTree T;//创建空的MST
int n;
int v;
//剩下的输入需要根据题目要求自己写
return 0;
}
Dijkstra 算法
理论解释:
实现的c语言代码:
#define maxWeight 32767
#define maxVertices 1000
typedef int weight ;
typedef struct
{
int u,v,w;
}Trituple;
typedef struct
{
int numVertices;
int Edge[maxVertices][maxVertices];
}MGraph;
void MinSpanTree_D(MGraph *G, int v0)
{
Trituple dist[maxVertices];//用dist[]数组记录构成MST的各个边的权值和节点
weight min;
int i,j,u,S[maxVertices];
for(i=0;i<G->numVertices;i++)
{
dist[i].u=v0,dist[i].v=i,dist[i].w=G->Edge[i][v0];
S[i]=0;
}
S[v0]=1,dist[v0].w=0,dist[v0].v=v0;
for(i=0;i<G->numVertices;i++)
{
if(i!=v0)
{
min=maxWeight;
for(j=0;j<G->numVertices;j++)
{
if(!S[j]&&min>dist[j].w)
{
u=j,min=dist[j].w;
}
}
S[u]=1,dist[u].v=u;
for(j=0;j<G->numVertices;j++)
{
if(!S[j]&&G->Edge[u][j]<dist[j].w)
{
dist[j].w=G->Edge[u][j];
dist[j].u=u;
}
}
}
}
}
int main()
{
MGraph G;
Trituple dist[maxVertices];
//根据题目具体情况去输入邻接矩阵
MinSpanTree_D(&G,0);
return 0;
}
(2) 最短路径
BFS算法
实现的c语言代码:
//G为无权重强连通图,用BFS求最短路径问题
#define Vertexmax 100
#define Maxsize 100
typedef char VertexType;
typedef int dataType;
typedef struct
{
VertexType Vertex[Vertexmax];
int AdjMAtirx[Vertexmax][Vertexmax];
int vexnum, arcnum;
}MGraph;
typedef struct
{
dataType *base;
int front;
int rear;
}CircQueue;
int LocateVex(MGraph *G,VertexType v)
{
int i;
for(i=0;i<G->vexnum;i++)
{
if(v==G->Vertex[i]) return i;
}
return -1;
}
void InitQueue(CircQueue *Q)
{
Q->base=(int *)malloc(Maxsize*sizeof(int));
if(!Q->base) return ;
Q->front=Q->rear=0;
return ;
}
void EnQueue(CircQueue *Q,int v)
{
if((Q->rear+1)%Maxsize==Q->front) return ;//full
Q->base[Q->rear]=v;
Q->rear=(Q->rear+1)%Maxsize;
return ;
}
void DeQueue(CircQueue *Q, int *v)
{
if(Q->front==Q->rear) return ;//empty
*v=Q->base[Q->front];
Q->front=(Q->front+1)%Maxsize;
return ;
}
int QueueEmpty(CircQueue *Q)
{
if(Q->front==Q->rear) return 1;
return 0;
}
void CreateUDG(MGraph *G)
{
int i,j;
scanf("%d %d", &G->vexnum,&G->arcnum);
scanf("%s",G->Vertex);
for(i=0;i<G->vexnum;i++)
{
for(j=0;j<G->vexnum;j++)
{
G->AdjMAtirx[i][j]=0;
}
}
int n,m;
VertexType v1,v2;
for(i=0;i<G->vexnum;i++)
{
scanf("%c,%c",&v1,&v2);
n=LocateVex(G,v1);
m=LocateVex(G, v2);
if(n==-1||m==-1) return;
G->AdjMAtirx[n][m]=G->AdjMAtirx[m][n]=1;
}
}
void BFS(MGraph *G, int v)
{
int j,i,k=v;
CircQueue Q;
InitQueue(&Q);
int visited[Maxsize];
for(i=0;i<G->vexnum;i++) visited[i]=0;
visited[v]=1;
EnQueue(&Q, v);
while(!QueueEmpty(&Q))
{
DeQueue(&Q, &k);
for(j=0;j<G->vexnum;j++)
{
if(G->AdjMAtirx[k][j]&&!visited[j])
{
visited[j]=1;
EnQueue(&Q, j);
}
}
}
}
int main()
{
MGraph G;
//其余的根据题目自己补充
BFS(&G,0);
return 0;
}
Dijkstra 算法
理论解释:
实现的c语言代码:
//dijkstra解决单源最短路径问题,path[j]=u表示在生成中的最短路径中j之后是u顶点
#define maxVertice 100
#define Weight int
#define maxWeight 32767
typedef struct
{
Weight AdjMatrix[maxVertice][maxVertice];
int numVertice,numEdge;
}MGraph;
void Dijkstra_SP(MGraph *G, int v, int *path)//path数组记录从节点i到节点v的最短路径上的后继节点
{
int i,j,u;
int visited[maxVertice];
Weight min;
Weight dist[maxVertice];
for(i=0;i<G->numVertice;i++)
{
visited[i]=0,dist[i]=G->AdjMatrix[v][i];
if(i!=v&&dist[i]<maxWeight) path[i]=v;
else path[i]=-1;
}
visited[v]=1;
for(i=0;i<G->numVertice;i++)
{
min=maxWeight,u=v;
if(i!=v)
{
for(j=0;j<G->numVertice;j++)
{
if(!visited[j]&&dist[j]<min)
{
u=j,min=dist[j];
}
}
}
visited[u]=1;
for(j=0;j<G->numVertice;j++)
{
if(!visited[u]&&dist[u]+G->AdjMatrix[j][u]<dist[j])
{
dist[j]=dist[u]+G->AdjMatrix[j][u];
path[j]=u;
}
}
}
}