单源最短路径问题

本文探讨了单源最短路径问题的性质和解决方案,特别是Dijkstra算法的应用。通过最优子结构的证明,阐述了最短路径的特点,并介绍了算法的实现步骤和实验结果分析。

单源最短路径问题

一、问题描述

  1. 如果存在一条从i到j的最短路径(Vi…Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径,求出最短路径。
  2. 分析:
    单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。
  3. 该性质描述为:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。
  4. 假设P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P’(k,s),那么P’(i,j)=P(i,k)+P’(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。
  5. 上述性质可知,如果存在一条从i到j的最短路径(Vi…Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra算法就是以最短路径长度递增,逐次生成最短路径的算法。
    譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。

二、程序实现

#include <iostream> 
#include<stack> 
#include<cstdlib> 
#define M 100 
#define N 100 
using namespace std; 
typedef struct node { 
    int matrix[N][M]; //邻接矩阵 
    int n; //顶点数 
    int e; //边数 
}MGraph; 
void DijkstraPath(MGraph g,int *dist,int *path,int v0) //v0表示源顶点 
{ 
    int i,j,k; 
    bool *visited=(bool *)malloc(sizeof(bool)*g.n); 
for(i=0;i<g.n;i++) //初始化 
{ 
  if(g.matrix[v0][i]>0&&i!=v0) { 
    dist[i]=g.matrix[v0][i]; 
    path[i]=v0; //path记录最短路径上从v0到i的前一个顶点 
} 
else {
 dist[i]=INT_MAX; //若i不与v0直接相邻,则权值置为无穷大 
 path[i]=-1; 
} 
 visited[i]=false; 
 path[v0]=v0; 
 dist[v0]=0;
  } 
  visited[v0]=true; 
  for(i=1;i<g.n;i++) //循环扩展n-1次 
  { 
  int min=INT_MAX; 
  int u; 
  for(j=0;j<g.n;j++) //寻找未被扩展的权值最小的顶点 
  { 
  if(visited[j]==false&&dist[j]<min) { 
  min=dist[j]; 
  u=j; 
  } 
  } 
  visited[u]=true; 
  for(k=0;k<g.n;k++) //更新dist数组的值和路径的值 
  { 
  if(visited[k]==false&&g.matrix[u][k]>0&&min+g.matrix[u][k]<dist[k]) { 
  dist[k]=min+g.matrix[u][k]; 
  path[k]=u; 
  } 
  } 
  } 
  } 
  void showPath(int *path,int v,int v0) //打印最短路径上的各个顶点 
  { 
  stack<int> s;
  int u=v;
while(v!=v0) { 
   s.push(v); 
   v=path[v]; 
   } 
   s.push(v); 
   while(!s.empty()) { 
    cout<<s.top()<<"  "; //表示输入的顶点数和边数 
    s.pop(); 
   } 
   } 
   int main(int argc, char *argv[]) { 
   int n,e; 
   cout<<"输入顶点数和边数: "<<endl;
   while(cin>>n>>e&&e!=0) { 
   int i,j; 
   int s,t,w; //表示存在一条边s->t,权值为w 
   MGraph g; 
   int v0; 
   int *dist=(int *)malloc(sizeof(int)*n);
   int *path=(int *)malloc(sizeof(int)*n); 
   for(i=0;i<N;i++) 
   for(j=0;j<M;j++) 
   g.matrix[i][j]=0; 
   g.n=n; 
   g.e=e; 
   cout<<"输入两个顶点及其之间边的权值: "<<endl;
   for(i=0;i<e;i++) { 
   cin>>s>>t>>w; 
   g.matrix[s][t]=w; 
   } 
   	cout<<"输入一个源顶点: "<<endl;
   cin>>v0; //输入源顶点 
   DijkstraPath(g,dist,path,v0); 
   cout<<"输出该源顶点到各顶点的最短路径及权值: "<<endl;
   for(i=0;i<n;i++) {
     if(i!=v0) { 
	showPath(path,i,v0); 
	cout<<dist[i]<<endl; 
	} 
	} 
	} 
	return 0; 
	}				pev[i] = start; // start可以直接到达i点
		}
		dis[start] = 0;
		vis[start] = 1;
		for (int i = 1; i < n; i++) {
			int min = MAX;
			int temp = start;
			for (int j = 1; j <= n; j++) {
				// 找与起始点相邻的最短的边
				if (vis[j] != 1 && dis[j] < min) {
					min = dis[j]; // 记录该最小值
					temp = j; // 该边对应的点
				}
			}
			vis[temp] = 1; // 该点已经找过
			for (int j = 1; j <= n; j++) {
				// 若要到达j点,经过此边会比不经过的距离更短
				if (vis[j] != 1 && dis[j] > dis[temp] + map[temp][j]) {
					// 经过此边,比不经过距离更短,更改到达j点的最短距离
					dis[j] = dis[temp] + map[temp][j];
					// 设置到达j点前必须先经过temp点
					pev[j] = temp;
				}
			}
		}
	}
}

三、实验结果与分析

在这里插入图片描述在这里插入图片描述

结果分析:

  • 通过此次实验可以总结出求单源最短路径的一般步骤为,假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。
  • 从V-U中选择使dist[i]值最小的顶点i,将i加入到U中,更新与i直接相邻顶点值,(dist[j]=min{dist[j],dist[i]+matrix[i][j]})直到U=V,停止。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值