解决最短路径问题 之 迪杰斯特拉(Dijkstra)算法 (距离+输出路径)

带权图

为了研究最短路径,首先引入带权边,带权图的概念:
设图G=(无向图或有向图),给定W:E->R,对于G的每一条边e,称W(e)为边e的,这样的图称为带权图,记为G=.
当e=(u,v)或e=时,把W(e)记作W(u,v).

设P是G中的一条通路,P中所有边的权之和称作P的长度,记作W(P). 即W(P)= ∑ \sum W(e) (e ∈ \in E(p)).
类似地可以定义回路C的长度W(C)

最短路径

设带权图G=,其中每一条边e的权W(e)为非负实数,对于V中任意两个顶点u,v,当uv连通时,称从u到v长度最短的路径为从u到v的最短路径,称其长度为从u到v的最短距离,记作d(u,v),约定d(u,u)=0,当u,v不连通时,d(u,v)= ∞ \infty
最短路径问题即是给定带权图G=以及顶点u,v,其中每一条边的权都为非负实数,求u到v的最短路径。

我们可以看出:若u v1 v2 v3…vk是u到v的最短路径,则对每一个t(1<=t<=k),u v1 v2 v3…vt是u到vt的最短路径。Dijkstra算法即是根据该条性质总结出的算法。

Dijkstra算法

附上教材给的过程,个人感觉还是有点难懂的:
算法给出了从给定某点s到每一个点的最短路径。在计算过程中,赋予每一个顶点v一个标号l(v)=(l1(v),l2(v)),标号分为永久标号与临时标号。在v的永久标号l(v)中,l2(v)是从s到v的距离,l1(v)是s到v的最短路径上v的前一个顶点,当l(v)是临时标号时,l1(v)和l2(v)分别是当前从s经过永久标号的顶点到v的最短路径上v的前一个顶点和这条路径的长度。
过程:
输入:带权图G和s ∈ \in V,其中|V|=n
输出:s到G中每一点的最短路径和距离
1.令ls<-(s,0),l(v)<-(s,+ ∞ \infty ) (v ∈ \in V-{s}),I<-1,l(s)是永久标号,其余标号均为临时标号,u<-s
2.for与u关联的临时标号的顶点v
3. if l2(u)+W(u,v) 4. 计算l2(t)=min{l2(v)|v ∈ \in V且有临时标号},并把l(s)改为永久标号
5. if i

分析:
需要分配的空间:
两个矩阵(二维数组)用于存放图和单源路径
两个一维数组存最短距离和访问标记
其他的一些数据
主要的思路个人感觉和贪心有点像,先找到源点可达的最短的路径,然后更新距离的数组,以到达的点为起点继续找最短路径,这样路径长度就能很简单地得到了。然后是路径输出,这个还想了一会儿,我的想法是用一个路径数组,每次判断距离数组要更新时,将记下的之前的最短路覆盖该路,然后再在末尾添加上新到达的结点。第一次初始化更新时需要同时初始化好路径数组的第一列和第二列(可达的项)。
上代码:

#include<iostream>
#include<vector>
#include<string.h>
using std::vector;
const int MAXN = 101;
const int INF = 0x3f3f3f3f;
//description:this is a template of Algorithm-Dijkstra
int min(int A,int B)
{
 if(A<B)return A;
 else return B; 
}//Get the min value
//
int m,n;
int visited[MAXN],Distance[MAXN];
int Matrix[MAXN][MAXN],Path[MAXN][MAXN];
//vector Path[MAXN];
//define 1.VisitSign 2.temp Distance(min) 3.The Graph 4.path
// 
void CreateGraph()
{
 memset(Path,0,sizeof(Path));
 memset(Matrix,INF,sizeof(Matrix));
 int Estart,Eend,Weight;
 std::cin>>n>>m;//input vertexnum and edgenum
 for(int i=1;i<=m;i++)
 {
  std::cin>>Estart>>Eend>>Weight;
  Matrix[Estart][Eend]=Weight;
  Matrix[Eend][Estart]=Weight;
  //assume that it is a normal graph
 }
 for(int i=1;i<=n;i++)
 {
  Matrix[i][i]=0;
 }
}//Get the Graph
//
void PrintGraph()
{
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
  {
   std::cout<<Matrix[i][j]<<" ";
  }
  std::cout<<"\n";
 }
}//print the graph to test
//
void Dijkstra(int st)
{
 memset(Distance,INF,sizeof(Distance));
 visited[st]=1;
 for(int i=1;i<=n;i++)
 {
  Path[i][1]=st;
  Distance[i]=min(Distance[i],Matrix[st][i]);
  if(Distance[i]==Matrix[st][i]&&i!=1)
  {
   Path[i][2]=i;//Initialize 
  }
 }
 for(int i=1;i<=n-1;i++)
 {
  int CurrentMinDis=INF;
  for(int j=1;j<=n;j++)
  {
   if(visited[j]==0&&CurrentMinDis>Distance[j])
   {
    CurrentMinDis=Distance[j];
    st=j;
   }
  }//find the minimum length path avaliable
  visited[st]=1;//sign "visited"
  for(int j=1;j<=n;j++)
  {
   if(Matrix[st][j]+Distance[st]<Distance[j])
   {
     for(int k=1;k<MAXN;k++)
     {
      Path[j][k]=Path[st][k];
      if(Path[st][k]==0)
      {
       Path[j][k]=j;
      break; 
      //update the path array
     }
     }
   }
   Distance[j]=min(Distance[j],Matrix[st][j]+Distance[st]);
   //update the distance array
  }
 }
}//Dijkstra Algorithm
//
int main()
{
/* vector::iterator iter1;*/
 CreateGraph();
 PrintGraph();
 Dijkstra(1);
 for(int i=1;i<=n;i++)
 {
  std::cout<<Distance[i]<<" ";
 }
 std::cout<<std::endl;
 std::cout<<std::endl;
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
  {
   if(Path[i][j]==0)
   {
    std::cout<<"end"<<std::endl;
    break; 
   }
   else
   {
    std::cout<<Path[i][j]<<"-";
   } 
  }
 }
 return 0;
}
弄了半个下午,好像路径输出上还是有点小问题,有时间再改改

测试:
5 6
1 2 5
1 3 8
2 3 1
2 4 3
4 5 7
2 5 2

7 11
1 2 3
2 5 6
5 7 2
7 6 2
6 4 2
4 1 5
1 3 7
3 4 3
3 5 3
2 3 2
4 7 8
一组是教材上的例题,一组是在网上找的>_<
解决最短路径问题 之 迪杰斯特拉(Dijkstra)算法 (距离+输出路径)_第1张图片
解决最短路径问题 之 迪杰斯特拉(Dijkstra)算法 (距离+输出路径)_第2张图片

你可能感兴趣的:(数据结构与算法,离散数学,Dijkstra,算法,离散数学,图论,最短路径)