Dijkstra迪杰斯特拉 算法详细步骤及实现

1,迪杰斯特拉算法介绍
迪杰斯特拉算法是典型最短路径算法,用于计算图或网中某个特定顶点到其他所有顶点的最短路径。主要特点是以起始点为中心向外,层层扩展,直到扩展覆盖所有顶点。
2,迪杰斯特拉算法思想
设G=(V,E)为一个带全有向图,把图中顶点集合V分成两组。第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将所到达最短路径的顶点加入到集合S中,直到全部顶点都加入到S中)。第二组为其余未确定最短路径的顶点集合(用U表示,U=V-S,U中的顶点不断的加入到S中,直到U为空,S=V)。在U加入S的过程中,始终保持源点到S中各顶点的最短路径长度小于或等于源点到U中任意顶点的最短路径长度。
3,迪杰斯特拉算法执行步骤
设 n 为图 G=(V,E) 中的顶点数,dist[n] 存放从源点到每个终点当前最短路径的长度,path[n] 存放相应路径,S 为已求得最短路径的终点的集合,U为V-S,初始为不含有源点的所有顶点。
(1)初始化已求的最短路径的集合S为只含有元素源点a,S={a}。
(2)从U中选取一个距离源点v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u(u U)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值为顶点k的距离加上顶点k到u边上的权。
(4)重复步骤(2)和(3)直到所有顶点都包含在S中。
4,迪杰斯特拉算法举例说明
(1)有向图如下,以a为源点,求源点a到其他各顶点的最短路径。
Dijkstra迪杰斯特拉 算法详细步骤及实现_第1张图片

(2)算法详细步骤如下表:

步骤
S集合中
U集合中
初始化
选入a,此时s={a}
此时最短路径有a->a=0
以a为中间点,从a开始找
U={b,c,d,e,f}
a->b=1
a->c=2
a->e=15
a->其他U中顶点为无穷
1
从U={b,c,d,e,f}中发现路径a->b=1最短
选入b,S={a,b}
此时最短路径有a->a=0,a->b=1
以b为中间点,从a->b=1这条最短路径开始找
U={c,d,e,f}
(a->b->d=7)<初始的无穷
改写a->b->d=无穷为当前的a->b->d=7
a-> b->其他U中顶点为无穷
2
从U={c,d,e,f}中发现路径a->c=2最短
选入c,S={a,b,c}
此时最短路径有
a->a=0,a->b=1,a->c=2
以b为中间点,从a->c=2这条最短路径开始找
U={d,e,f}
(a->c->d=5)<已有的7
改写为a->c->d=5
3
从U={d,e,f}中发现路径a->c->d=5最短
选入d,S={a,b,c,d}
此时最短路径有
a->a=0,a->b=1,a->c=2,a->c->d=5
以d为中间点,从a->c->d=5这条最短路径开始找
 
 
U={e,f}
(a->c->d->e=9)<步骤1中的15
改写为a->c->d->e=9
(a->c->d->f=6)<初始的无穷
改写为a->c->d->f=6
 
4
从U={e,f}中发现路径a->c->d->f=6最短
选入f,S={a,b,c,d,f}
此时最短路径有
a->a=0,a->b=1,a->c=2,a->c->d=5
a->c->d->f=6
以f为中间点,从a->c->d->f=6这条最短路径开始找
 
U={e}
(a->c->d->f->e=7)<步骤3中改写成的9
改写为a->c->d->f->e=7
 
5
从U={f}中发现路径a->c->d->f->e=7最短
选入f,S={a,b,c,d,f,e}
此时最短路径有
a->a=0,a->b=1,a->c=2,a->c->d=5
a->c->d->f=6,a->c->d->f->e=7
U集合已空,查找完毕。



例如,对下图中的有向图,应用Dijkstra算法计算从源顶点1到其它顶点间最短路径的过程列在下表中。

Dijkstra迪杰斯特拉 算法详细步骤及实现_第2张图片

Dijkstra算法的迭代过程:

Dijkstra迪杰斯特拉 算法详细步骤及实现_第3张图片

主题好好理解上图!

以下是具体的实现(C/C++):

 
#include 
< iostream >
using   namespace  std;
 
const   int  maxnum  =   100 ;
const   int  maxint  =   999999 ;
 
 
void  Dijkstra( int  n,  int  v,  int   * dist,  int   * prev,  int  c[maxnum][maxnum])
{
    
bool  s[maxnum];     //  判断是否已存入该点到S集合中
     for ( int  i = 1 ; i <= n;  ++ i)
    {
        dist[i] 
=  c[v][i];   //初始化其他点到源点的最短距离
        s[i] 
=   0 ;             //  初始都未用过该点
         if (dist[i]  ==  maxint)   //初始化其他点距离源点最短路径的    前一个点
            prev[i] 
=   0 ;    //其他点到源点   不存在路径时,
        
else
            prev[i] 
=  v;
    }
    //初始化源点的状态
    dist[v] 
=   0 ;
    s[v] 
=   1 ;
 
    
//  依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
    
//  一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
     for ( int  i = 2 ; i <= n;  ++ i)
    {
        
int  tmp  =  maxint;
        
int  u  =  v;
        
//  找出当前未使用的点j的dist[j]最小值
         for ( int  j = 1 ; j <= n;  ++ j)
            
if (( ! s[j])  &&  dist[j] < tmp)
            {
                u 
=  j;               //  u保存当前邻接点中距离最小的点的号码
                tmp  =  dist[j];
            }
        s[u] 
=   1 ;     //  表示u点已存入S集合中
 
        
//  更新其他点距离源点 最短距离点的 dist
         for ( int  j = 1 ; j <= n;  ++ j)
            
if (( ! s[j])  &&  c[u][j] < maxint)
            {
                
int  newdist  =  dist[u]  +  c[u][j];
                
if (newdist  <  dist[j])
                {
                    dist[j] 
=  newdist;
                    prev[j] 
=  u;
                }
            }
    }
}
 
void  searchPath( int   * prev, int  v,  int  u)
{
    
int  que[maxnum];
    
int  tot  =   1 ;
    que[tot] 
=  u;
    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 )
            cout 
<<  que[i]  <<   "  ->  " ;
        
else
            cout 
<<  que[i]  <<  endl;
}
 
int  main()
{
    freopen(
" input.txt " " r " , stdin);
    
//  各数组都从下标1开始
     int  dist[maxnum];      //  表示当前点到源点的最短路径长度
     int  prev[maxnum];      //  记录当前点的前一个结点
     int  c[maxnum][maxnum];    //  记录图的两点间路径长度
     int  n, line;              //  图的结点数和路径数
 
    
//  输入结点数
    cin  >>  n;
    
//  输入路径数
    cin  >>  line;
    
int  p, q, len;           //  输入p, q两点及其路径长度
 
    
//  初始化c[][]为maxint
     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)  
    {
        cin 
>>  p  >>  q  >>  len;
        
if (len  <  c[p][q])        //  有重边
        {
            c[p][q] 
=  len;       //  p指向q
            c[q][p]  =  len;       //  q指向p,这样表示无向图
        }
    }
 
    
for ( int  i = 1 ; i <= n;  ++ i)
        dist[i] 
=  maxint;
    
for ( int  i = 1 ; i <= n;  ++ i)
    {
        
for ( int  j = 1 ; j <= n;  ++ j)
            printf(
" %8d " , c[i][j]);
        printf(
" \n " );
    }
 
    Dijkstra(n, 
1 , dist, prev, c);
 
    
//  最短路径长度
    cout  <<   " 源点到最后一个顶点的最短路径长度:  "   <<  dist[n]  <<  endl;
 
    
//  路径
    cout  <<   " 源点到最后一个顶点的路径为:  " ;
    searchPath(prev, 
1 , n);
}

输入数据:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
输出数据:
999999 10 999999 30 100
10 999999 50 999999 999999
999999 50 999999 20 10
30 999999 20 999999 60
100 999999 10 60 999999
源点到最后一个顶点的最短路径长度: 60
源点到最后一个顶点的路径为: 1 -> 4 -> 3 -> 5

以下是java实现

import java.util.ArrayList;
import java.util.LinkedHashMap;
public class DijkstraPath {
/**
* @param args
*/
static int[][] cost;
static ArrayList<String> visited = new ArrayList<String>();
static ArrayList<String> unVisited = new ArrayList<String>();
static ArrayList<String> vertexs = new ArrayList<String>();
static LinkedHashMap<String ,Integer> shortPath = new LinkedHashMap<String ,Integer>();
static ArrayList<arc> arcs = new ArrayList<arc>();
static LinkedHashMap<String ,String> shortPathWay = new LinkedHashMap<String ,String>();
public static void main(String[] args) {
   // TODO Auto-generated method stub
        //init the verges set 
  
   arcs.add(new arc("A","B",2));
   arcs.add(new arc("A","C",4));
   arcs.add(new arc("A","D",15));
   arcs.add(new arc("B","D",5));
   arcs.add(new arc("B","C",1));
   arcs.add(new arc("C","D",7));
   arcs.add(new arc("D","E",4));
  
   //init the nodes set
   vertexs.add("A");
   vertexs.add("B");
   vertexs.add("C");
   vertexs.add("D");
   vertexs.add("E");
  
  
   // init the novisited set
   visited.add("A");
  
   //init the visited set
   unVisited.add("B");
   unVisited.add("C");
   unVisited.add("D");
   unVisited.add("E");
  
  
   //init the shortPath map
   for(String unvisitNode:unVisited)
   {
   
    boolean access = false;
    for(arc a:arcs)
    {
     if(a.startNode.equals("A") && a.endNode.equals(unvisitNode))
     {    
      shortPath.put(unvisitNode,a.weight);
      access = true;
      break;
     
     }
    
    }
    if(access == false)
    {
     shortPath.put(unvisitNode, -1);
    }
   }
//把第一个临近节点的前驱找到 
   initFirstShortPathWay();
   
       while(unVisited.size()>0){
        String lastVisitedNode = getLastVisitedNode();
        
        for(String unvisitNode:unVisited)
        {
          //获得最后一访问节点到未访问节点到距离
          int newPath = getWeight(lastVisitedNode,unvisitNode);
          if(newPath > 0)
          {
          //获得源点到未访问节点的距离
          int oldPath = getOldPath(unvisitNode);
          //如果二者都存在话,改变shortPath 的相应值为最小值
          if(oldPath > 0)
          {
             if(oldPath > getOldPath(lastVisitedNode)+newPath){                       
              resetShortPath(unvisitNode,getOldPath(lastVisitedNode)+newPath);
              shortPathWay.put(unvisitNode,lastVisitedNode);//后继——前驱
             }
          }
          //如果原来不可达的话,但是通过中间节点可以到达,那么同样要改变shortPath
          else
          {
             resetShortPath(unvisitNode,getOldPath(lastVisitedNode)+newPath);
               shortPathWay.put(unvisitNode,lastVisitedNode);
            
          }
          
          }
        }
        String minNode = getTheMinPathNode();
        
        removeNode(minNode,unVisited);
        addNode(minNode,visited);
       
     }   
       //输出最终结果
       printResult();



//初始化第一个 路径的前驱
public static void initFirstShortPathWay()
{
      int min = 500;
      String firstNode ="";
    for(String vertex:shortPath.keySet())
    {
     int tem = shortPath.get(vertex);
     if(tem > 0){
      min = min > tem?tem:min;
     }
    
    }
      for(String vertex:shortPath.keySet())
      {
      if(min == shortPath.get(vertex))firstNode = vertex;
      }    
      shortPathWay.put(firstNode,"A"); 
  
}
//add a node to the set
public static void addNode(String node,ArrayList<String> set)
{
   set.add(node);
}
// remove a node of the set
public static void removeNode(String delNode,ArrayList<String> set){
   int index = 0;
   for(int i=0;i<set.size();i++)
   {
    if(delNode.equals(set.get(i)))
    {
     index = i;
    }
   }
   set.remove(index);
  
}
//得到未访问结点中shutPath的最小值的点
public static String getTheMinPathNode()
{
   int min = 500; //距离超过500为不可达
   String node = "";
   for(String unode:unVisited)
   {
    int tem = shortPath.get(unode);
    if(tem>0){
     min = min>tem?tem:min;    
    }   
   }
   for(String unode:unVisited)
   {
           
    if(min == shortPath.get(unode))node=unode;
   }
   return node;
}


//得到源点到未访问结点的最短距离
public static int getOldPath(String node)

   if(node.equals("A"))return 0;
   return shortPath.get(node);
}
//重新设定 shortPath
public static void resetShortPath(String node,int path)
{
   for(String snode:shortPath.keySet())
   {
    if(snode.equals(node))shortPath.put(snode, path);
   }
}
    //get the last node of the visited set
public static String getLastVisitedNode()
{
   return visited.get(visited.size()-1);
}


// get the weight
public static int getWeight(String startNode, String endNode)
{
   int weight=-1;
   for(arc a:arcs)
   {
    if(a.startNode.equals(startNode) && a.endNode.equals(endNode))
    {
     weight = a.weight;
     System.out.println(a.startNode+"-->"+a.endNode+"="+weight);
    }
   }
  
   return weight;
}
// get the min num
public static void printResult()
{
   for(String vertex:shortPath.keySet())
   {
    System.out.print("从源点A到"+vertex+"的最短路径为");
    printPath(vertex);
    System.out.print(vertex);
    System.out.print("长度为:"+shortPath.get(vertex));
    System.out.println(" ");
   }
}


   public static void printPath(String vertex)
   {
    String node = shortPathWay.get(vertex);
    if(!node.equals("A"))printPath(node);
    System.out.print(node+" ");
   }
}
class arc{
String startNode = "";
String endNode = "";
int weight =0;
public arc(String startNode,String endNode,int weight){
   this.startNode = startNode;
   this.endNode = endNode;
   this.weight = weight; 
}


}



你可能感兴趣的:(Dijkstra迪杰斯特拉 算法详细步骤及实现)