<p><span style="font-size: 18px;">最短路主要有四部分。</span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span>一</span><span style="font-family: Arial, Helvetica, sans-serif;">迪杰斯特拉思想</span></p>
//Dijkstra算法用于计算一个节点到其它所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点。
//基本思想是:设置顶点集合S不断地做贪心选择来扩充这个集合
//初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。
#include <stdio.h>
#define maxnum 100
#define maxint 999999
int dist[maxnum]; //表示当前点到源点的最短路径长度
int prev[maxnum]; //记录当前点的前一个结点
int c[maxnum][maxnum]; //记录图的两点间路径长度
int n,line; //图的结点数和路径数
void Dij(int n,int v,int *dist, int *prev, int c[maxnum][maxnum]) //v代表源点吗?
{
int s[maxnum] = {0};//判断是否已将该点存入到s数组中
int i;
for(i = 1;i<=n;i++)
{
dist[i] = c[v][i];//将从源点到i点的初始距离存入数组dist中
s[i] = 0;//初始化为全部没有用过该点
if(dist[i] == maxint)//如果源点到该点的距离为无穷大,则该点前面没有连接点
prev[i] = 0;
else
prev[i] = v;
}
dist[v] = 0; //源点到源点的距离初始化为0
s[v] = 1;//把源点存入数组s
//依次将未放入集合s中的节点,取dist【】最小值的节点放进去
//一旦s包含了所有v中顶点,dist就记录了从源点到所有其他顶点之间的最短路径
//注意是从第二个节点开始,第一个结点表示源点
for(i = 2;i<=n;i++)
{
int tmp = maxint;
int u = v;
for(int j = 1;j<=n;j++) //找出当前未使用的点j的dist【j】最小值
{
if((!s[j]) && dist[j] < tmp)
{
u = j; //u储存当前邻接点中距离最小的点的号码
tmp = dist[j];
}
}
s[u] = 1; //一次for循环结束后,便找出了所有未使用的点中距离最小的点,将此点存入集合s中
//更新dist值
for(int j = 1;j<=n;j++)
{
if((!s[j]) && c[u][j]<maxint) //确定该点没有加入s集合,并且存在这段距离,即经过该点
{
int newdist = dist[u] + c[u][j];//判断经过该点到j的距离和不经过该点到j的距离的大小
if(newdist < dist[j])
{
dist[j] = newdist;
prev[j] = u;
}
}
}
}
}
//查找从源点v到终点u的路径,并输出
void searc(int *prev,int v,int u)
{
int que[maxnum]; //该数组保存路径
int tot = 1;
que[tot] = u; //从终点开始找,一直找到源点v并记录路径
tot++;
int tmp = prev[u];
while(tmp != v)
{
que[tot] = tmp;
tot++;
tmp = prev[tmp];
}
que[tot] = v;
for(int i = tot;i>=1;i--)//输出
{
if(i!=1)
printf("%d->",que[i]);
else
printf("%d\n",que[i]);
}
//注意,若题目要求输出距离,则直接输出dist【u】即可
}
int main()
{
scanf("%d%d",&n,&line);
int p,q,len;
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
c[i][j] = maxint;
}
for(int i = 1;i<=line;i++)
{
dist[i] = maxint;
}
for(int i = 1;i<=line;i++)
{
printf("%d%d%d",&p,&q,&len);
if(len<c[p][q])
{
c[p][q] = len;
c[q][p] = len;
}
}
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
printf("%8d",c[i][j]);
}
printf("\n");
}
Dij(n,1,dist,prev,c);
printf("从源点到终点的最短路径长度为%d\n",dist[n]);
searc(prev,1,n);
return 0;
}
//下面运用弗洛伊德(Floyd)来解决有关问题
//在该思想中,数组a【】【】初始化为个顶点之间的距离,最后储存各定点之间的最短距离。
//path【】【】数组保存最短路径,与当前迭代的次数有关,初始化都为-1,即没有中间顶点。
//在求a【i】【j】的过程中,path[i][j]存放从顶点i到顶点j的中间顶点的编号不大于k的最短路径上前一个结点的编号
//数组a[][]其实是有向图的邻接矩阵,此类矩阵可以表示无向
//这个程序是
#include <stdio.h>
#include <string.h>
struct MGraph
{
char vertex[1000]; // 储存各个顶点
int edges[1000][1000]; //邻接矩阵
int n,e; // n代表顶点个数,e代表边数
};
void ppath(int path[][1000],int i,int j) 调用函数,输出路径
{
int k;
k = path[i][j];
if(k == -1)
return;
ppath(path,i,k);
printf("%d,",k);
ppath(path,k,j);
}
void CreateMGraph(MGraph &G)
{
int i,j,k,p;
scanf("%d%d",&G.n,&G.e); //输入顶点数和边数
for(i = 0;i<G.n;i++) //输入各个顶点
{
scanf("%d",&G.vertex[i]);
}
for(i = 0;i<G.n;i++) // 初始化邻接矩阵。若是对角线,即是自己到自己的距离,初始化为0
{
for(j = 0;j<G.n;j++)
{
G.edges[i][j] = 100000;
if(i == j)
G.edges[i][j] = 0;
}
}
for(i = 0;i<G.e;i++) // 输入边的前驱后驱以及权值
{
scanf("%d%d%d",&j,&k,&p);
G.edges[j][k] = p;
}
}
void Dispath(int A[][1000],int path[][1000],int n)
{
int i,j;
for(i = 0;i<n;i++)
{
for(j = 0;j<n;j++)
{
if(A[i][j] == 100000)
{
if(i!=j)
printf("从%d到%d没有路径\n",i,j);
}
else
{
printf("从%d到%d=>路径长度:%d路径:",i,j,A[i][j]);
printf("%d",i);
ppath(path,i,j);
printf("%d\n",j);
}
}
}
}
void Floyd(MGraph &G)
{
int A[1000][1000],path[1000][1000];
int i,j,k;
//初始化a【】【】为最开始的距离,path【】【】代表最短路径,与当前迭代的次数有关
for(i = 0;i<G.n;i++)
{
for(j = 0;j<G.n;j++)
{
A[i][j] = G.edges[i][j];
path[i][j] = -1;
}
}
for(k = 0;k<G.n;k++)// 运用弗洛伊德进行最短路搜索,k作为中间点
{
for(i = 0;i<G.n;i++)
{
for(j = 0;j<G.n;j++)
{
if(A[i][j] > A[i][k] + A[k][j])
{
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
}
}
}
Dispath(A,path,G.n);
}
int main()
{
MGraph G;
CreateMGraph(G);
Floyd(G);
return 0;
}
</pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">
</pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">
贝尔曼—福特思想
//迪杰斯特拉算法是处理单源最短路径的算法,但它局限于边的权值非负的情况
//贝尔曼——福特就可以解决此类问题
//思想:给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,
//数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为, Distant[s]为0;
//以下操作循环执行至多n-1次,n为顶点数:
//对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
//若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
//为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,
//即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。
#include <stdio.h>
#include <stdlib.h>
#define maxnum 2000
#define maxint 999999
typedef struct
{
int v,u; //终点和起点
int weight; //两点之间的权值
} Edge;
Edge edge[maxnum]; //保存边的值
int dist[maxnum]; //保存结点到原点的最短距离
int nodenum,edgenum,source; //结点数,边数,源点
void init()
{
scanf("%d%d%d",&nodenum,&edgenum,&source); //输入结点数,边数,源点
int i;
for(i = 1;i <= nodenum;i++)
dist[i] = maxint; //把结点到原点的距离都初始化为最大
dist[source] = 0; //节点到节点的距离初始化为0
for(i = 1;i<=edgenum;i++)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].weight); //点u到点v的距离
if(edge[i].u == source)
dist[edge[i].v] = edge[i].weight; //如果起点是源点,则把点v到点u(源点)的距离更改为权值weight
}
}
int BF() //该函数用于计算最小距离和判断是否有负权值
{
int i,j,flag = 1;
for(i = 1;i<=nodenum-1;i++) //外层循环的次数是结点数减一
{
for(j = 1;j <= edgenum;j++)//内层循环是边的数目,也就是每次都遍历整个边表
{
if(dist[edge[j].v] > dist[edge[j].u] + edge[j].weight)
dist[edge[j].v] = dist[edge[j].u] + edge[j].weight;
//如果从源点到第v个点的距离大于从源点到第u个点的距离加上从u点到v点的距离,就改变从源点到第v个点的距离
}
}
for(i = 1;i<=edgenum;i++) //再一次进行最短路搜寻,在上面的循环中已经求出了最短路线,如果还能求出最短路线,则会出现负环路,每进去一次,都会减小
{
if(dist[edge[i].v > dist[edge[i].u + edge[i].weight)
{
flag=0;
break;
}
}
return flag;
}
int main()
{
init();
if(BF())
{
for(int i = 1;i<=nodenum;i++)
printf("%d\n",dist[i]);
}
else
printf("-1\n");
return 0;
}