prim求最短路径C语言,[图论]Prim算法求最小支撑树和最短路径

这个是以前所学,现在总结成博文一篇。

对于图论中的求解最小支撑树问题和最短路径问题都有比较经典的算法,比如最小支撑树可以采用“破圈法”(kruskal算法),求解最短路径可以用“Dijkstra算法”。这里笔者将回顾下求解最小支撑树的Prim算法和最短路径算法。

1、Prim算法求解最小支撑树

例1:用Prim算法求解图1的一个最小支撑树。

780db8727e2ee80b0c41d79f117ceb2e.png

图1:例图

首先,给出Prim算法:

c5c88a57c9974c50ad9d15bc152395f4.jpg

实现的C语言代码如下:

#include

int matrix[100][100];

int E[100]={0},tree[100][100]={0};

int n,i,j;

void inputmatrix()

{

printf("请输入邻接矩阵的阶数:\n");

scanf("%d",&n);

printf("请输入邻接矩阵:\n");

for(i=0;i

{

for(j=0;j

scanf("%d",&matrix[i][j]);

}

}

void caculate(int n,int a[100][100])

{

int k=n-1,t=0,t1,t2,min,j,m=1,flag=1;/*k,k1作为循环的次数,flag标记是否有支撑树*/

E[0]=1; /*先放入一个点*/

tree[0][0]=a[0][0];

while(k--)

{

min=100000;

for(j=0;j

{

for(i=0;i

if(E[i]==1&&E[j]==0&&min>a[i][j]&&a[i][j]!=0) /*寻找符合条件的数*/

{

t1=j;

t2=i;

min=a[i][j];

}

}

E[t1]=1; /*标记为1,代表已经用过*/

tree[t2][t1]=a[t2][t1]; /*记录值*/

tree[t1][t2]=a[t1][t2]; /*记录值*/

m++;

}

for(i=0;i

{

if(E[i]==0)

{

printf("没有支撑树\n");

flag=0;

break;

}

}

if(flag==1) /*证明存在支撑树*/

{

printf("输出对应的最小支撑树所对应的矩阵:\n");

for(i=0;i

{

for(j=0;j

printf("%d ",tree[i][j]);

printf("\n");

}

}

}

main()

{

inputmatrix();

caculate(n,matrix);

system("pause");

}

针对图1的例子,我们输入数据求得结果:

3a5c2ef0c1526224bf40f5d0672958f6.png

2、改进的Prim算法求解最短路径

例2:利用改进的Prim算法求解图2的最短路径问题。

bf266a33f47d49aea9970cf59b2eaa26.jpg

图2:例图

首先给出该算法的描述。

2da7bf867fe64dfb8dd2f83673a12df0.jpgc455aac911439c7f91bcb2e1773aab81.png

下面是该算法的C语言实现。

#include

int matrix[100][100];

int E[100]={0},distance[100]={0};

int path[100][100] = {0};//初试为空,记录path

int n,i,j,ini;//ini为初始下标

char temp[100];

void inputmatrix()

{

printf("请输入邻接矩阵的阶数:\n");

scanf("%d",&n);

printf("请输入有向图的邻接矩阵(输入0代表无穷大):\n");

for(i=0;i

{

for(j=0;j

scanf("%d",&matrix[i][j]);

}

printf("请输入需计算的起始点下标:\n");

scanf("%d",&ini);

E[ini-1]=1;

path[ini-1][0]=ini;

}

void caculate(int n,int a[100][100])

{

int k=n-1,t=0,t1,t2,min,j,m=1,w,flag=0;/*k,k1作为循环的次数,flag=1意味着可以继续循环,flag=0,跳出*/

while(k--)

{

min=100000;

flag=0;// 初始化

for(j=0;j

{

for(i=0;i

if(E[i]==1&&E[j]==0&&min>(a[i][j]+distance[i])&&a[i][j]>0) /*寻找符合条件的数*/

{

flag=1;// 标记为1,找到!继续循环

t1=j;

t2=i;

min=a[i][j]+distance[i];

}

}

if(flag==0)

break;//跳出

distance[t1]=min;

E[t1]=1; /*标记为1,代表已经用过*/

for(j=0;j

{

if(path[t2][j]!=0)

{

path[t1][j]=path[t2][j];

}

else

{

path[t1][j]=t1+1;

break;

}

}

m++;

}

printf("\n\n----下面为反圈法(prim法)计算结果----\n\n",ini,i+1,distance[i]);

for(i=0;i

{

if(E[i]==1)

{

printf("点%d到点%d的最小距离:%d\n",ini,i+1,distance[i]);

printf("其最短路径:");

for(j=0;j

{

if(path[i][j]!=0)

{

printf("v%d ",path[i][j]);

}

else break;

}

printf("\n");

}

else

printf("点%d到点%d没有路可通!\n\n",ini,i+1);

}

}

main()

{

inputmatrix();

caculate(n,matrix);

system("pause");

}

针对图2,给出运行结果:

203a721d8f484a73a2f823382ab2ffa4.jpg

笔者水平有限,难免有不足,请批评指正!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言最小生成树和关键路径的算法有很多,下面我会介绍两种常用的算法。 1. 最小生成树 最小生成树是一种图论中的算法,用于在一个加权连通图中找到一棵生成树,使得树上所有边的权值之和最小。常用的算法Prim算法和Kruskal算法。 (1)Prim算法 Prim算法是一种贪心算法,它的基本思想是从一个点开始,每次选择一个与当前生成树相邻的权值最小的点加入生成树中,直到所有点都被加入为止。具体实现可以使用堆优化的Prim算法,时间复杂度为O(ElogV)。 以下是Prim算法C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 typedef struct { int v, w; } Edge; typedef struct { int n; Edge edges[MAX_VERTICES][MAX_VERTICES]; } Graph; void prim(Graph *g, int start) { int i, j, u, v, min; int dist[MAX_VERTICES], visited[MAX_VERTICES]; for (i = 0; i < g->n; i++) { dist[i] = INT_MAX; visited[i] = 0; } dist[start] = 0; for (i = 0; i < g->n; i++) { min = INT_MAX; for (j = 0; j < g->n; j++) { if (!visited[j] && dist[j] < min) { min = dist[j]; u = j; } } visited[u] = 1; for (v = 0; v < g->n; v++) { if (g->edges[u][v].w > 0 && !visited[v] && g->edges[u][v].w < dist[v]) { dist[v] = g->edges[u][v].w; } } } } int main() { Graph g; int i, j, n, m, u, v, w; scanf("%d%d", &n, &m); g.n = n; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { g.edges[i][j].w = 0; } } for (i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &w); g.edges[u][v].w = w; g.edges[v][u].w = w; } prim(&g, 0); return 0; } ``` (2)Kruskal算法 Kruskal算法也是一种贪心算法,它的基本思想是将所有边按照权值从小到大排序,然后依次加入生成树中,如果加入一条边会形成环,则不加入。具体实现可以使用并查集来判断是否形成环,时间复杂度为O(ElogE)。 以下是Kruskal算法C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 #define MAX_EDGES 1000000 typedef struct { int u, v, w; } Edge; typedef struct { int n, m; Edge edges[MAX_EDGES]; } Graph; int cmp(const void *a, const void *b) { return ((Edge *)a)->w - ((Edge *)b)->w; } int find(int *parent, int i) { if (parent[i] == -1) { return i; } return find(parent, parent[i]); } void union_set(int *parent, int x, int y) { int xroot = find(parent, x); int yroot = find(parent, y); parent[xroot] = yroot; } void kruskal(Graph *g) { int i, j, k, u, v, w; int parent[MAX_VERTICES]; for (i = 0; i < g->n; i++) { parent[i] = -1; } qsort(g->edges, g->m, sizeof(Edge), cmp); for (i = 0, j = 0; i < g->n - 1 && j < g->m; j++) { u = g->edges[j].u; v = g->edges[j].v; w = g->edges[j].w; if (find(parent, u) != find(parent, v)) { union_set(parent, u, v); i++; } } } int main() { Graph g; int i, n, m, u, v, w; scanf("%d%d", &n, &m); g.n = n; g.m = m; for (i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &w); g.edges[i].u = u; g.edges[i].v = v; g.edges[i].w = w; } kruskal(&g); return 0; } ``` 2. 关键路径 关键路径是指在一个有向无环图中,从起点到终点的所有路径中,耗时最长的路径。关键路径的算法有很多,下面介绍一种常用的算法——拓扑排序。 (1)拓扑排序 拓扑排序是一种基于有向无环图的排序算法,它的基本思想是将图中的所有节点按照一定的顺序排列,使得所有的有向边都从前面的节点指向后面的节点。具体实现可以使用DFS或BFS,时间复杂度为O(V+E)。 以下是拓扑排序关键路径的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 #define MAX_EDGES 1000000 typedef struct { int v, w; } Edge; typedef struct { int n, m; Edge edges[MAX_EDGES]; int indegree[MAX_VERTICES]; } Graph; void topological_sort(Graph *g, int *dist) { int i, j, u, v, w; int queue[MAX_VERTICES], front = 0, rear = 0; for (i = 0; i < g->n; i++) { if (g->indegree[i] == 0) { queue[rear++] = i; dist[i] = 0; } } while (front < rear) { u = queue[front++]; for (i = 0; i < g->m; i++) { v = g->edges[i].v; w = g->edges[i].w; if (g->edges[i].u == u) { if (--g->indegree[v] == 0) { queue[rear++] = v; dist[v] = dist[u] + w; } } } } } int main() { Graph g; int i, n, m, u, v, w; int dist[MAX_VERTICES], earliest[MAX_VERTICES], latest[MAX_VERTICES]; scanf("%d%d", &n, &m); g.n = n; g.m = m; for (i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &w); g.edges[i].u = u; g.edges[i].v = v; g.edges[i].w = w; g.indegree[v]++; } topological_sort(&g, dist); for (i = 0; i < g.n; i++) { earliest[i] = dist[i]; } for (i = 0; i < g.m; i++) { u = g.edges[i].u; v = g.edges[i].v; w = g.edges[i].w; if (earliest[u] + w > earliest[v]) { earliest[v] = earliest[u] + w; } } for (i = 0; i < g.n; i++) { latest[i] = INT_MAX; } latest[n - 1] = earliest[n - 1]; for (i = g.m - 1; i >= 0; i--) { u = g.edges[i].u; v = g.edges[i].v; w = g.edges[i].w; if (latest[v] - w < latest[u]) { latest[u] = latest[v] - w; } } for (i = 0; i < g.m; i++) { u = g.edges[i].u; v = g.edges[i].v; w = g.edges[i].w; if (earliest[u] + w == earliest[v] && latest[u] + w == latest[v]) { printf("%d %d %d\n", u, v, w); } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值