最短路径—Dijkstra算法(C#)

前言 :

因为之前刚写过最小生成树算法,刚开始看到Dijkstra算法的时候,因为要求各点到源点的最短距离,会想着直接从最小生成树的也是每找到最短的距离点加进来,但是后面仔细想了下,又翻了下定义,得到

最短路径是一个图中2个点的最短距离。

最小生成树是连接所有的点的路径最短,但是不一定是任意两点的距离最小,例如:

 

 

最小生成树是:A->B->C AC距离是2+2=4

但是AC的最短距离是3

一 最短路径问题

最短路径问题是图论研究中的一个经典算法问题,旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。算法具体的形式包括:

1.确定起点的最短路径问题:即已知起始结点,求最短路径的问题。适合使用Dijkstra算法。

2.确定终点的最短路径问题:与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。

3.确定起点终点的最短路径问题:即已知起点和终点,求两结点之间的最短路径。

4.全局最短路径问题:求图中所有的最短路径。适合使用Floyd算法。

这里我们在这篇中只分析第一种,第四种在下一篇博客将会给出。

二 Dijkstra定义概述

 

Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止(BFS、prime算法都有类似思想)。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。时间复杂度为O(n^2)。问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)

三算法描述

 

3.1 算法思想

 

设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

3.2 算法步骤:

 

a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。

b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。

c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

d.重复步骤b和c直到所有顶点都包含在S中。

3.3 具体算法实现:

 

3.1和3.2的内容都是书上所描述的,这里之所以写到,是想让大家先理解源点到各点的最短路径的算法思想大致是怎样的?但是对于如何从U中选取一个距离v最小的点是没有具体方案的,具体用算法实现的时候,这里我是巧妙的利用了邻接矩阵来做的。大致思路:首先我们用邻接矩阵来存图,我们的最终目的是要求V1(源点)到其他点的最小距离,体现在邻接矩阵中,V0(源点)到其他点的初始距离就是第一行的元素值,如果我们想通过各种变换,运算,改变第一行的元素值使其是V0(源点)到其他点的最小距离,这样就很简单了,即通过3.1 3.2的算法思路,来进行邻接矩阵上的操作,使其第一行的元素值就是V0(源点)到其他点的最小距离。

四 算例

有向图如下:

 

邻接矩阵为graph:

 

 

V0

V1

V2

V3

V4

V5

V0

10000

10000

10

10000

30

100

V1

10000

10000

5

10000

10000

10000

V2

10000

10000

10000

50

10000

10000

V3

10000

10000

10000

10000

20

10

V4

10000

10000

10000

10000

10000

60

V5

10000

10000

10000

10000

10000

1000

 

 

 

4.1 变量介绍:

邻接矩阵数组:graph,

下一个点:next,最小值min,中间路线图数组(6个点的)mid

4.2 整体思路:

1)首先将集合V0添加入S集合,遍历第一行(graph[0][j]),找到最小的值对应的列值点j.

2)记录数据:将下一个点next =j 添加入S集合,V0-->Vj最小值min=graph[0][j]。

3)重新初始第一行的值:循环graph[next][j],判断(graph[next][j]+min)是否小于graph[0][j](j不在S集合中),若是,则替换后者并记录过程 mid[j] = mid[next] + "->V" + next;

4)循环1),2),3)步骤,直至循环次数为点的个数-1,即6-1=5。

 

4.3 图形化步骤如下:

(1) 找到第一行的最小值min=10,得到下一个点Next=2,S={0,2},路径为:V0->V2

(2)将V2行的值加上min=10,与第一行V0,V2内的元素值对比,若小于,则替换.

(替换了graph[0][4]=60)。变换为下面矩阵,然后继续找第一行除V0,V2外的最小值min为30,得到下一个点Next=4,S={0,2,4},路径为:V0->V4

 

 

V0

V1

V2

V3

V4

V5

V0

10000

10000

10

60

30

100

V1

10000

10000

5

10000

10000

10000

V2

10000

10000

10000

50

10000

10000

V3

10000

10000

10000

10000

20

10

V4

10000

10000

10000

10000

10000 

V5

10000

10000

10000

10000

10000

1000


(3)将V4行的值加上min=30,与第一行除V0,V2,V4内的元素值对比,若小于,则替换.

 

(替换了graph[0][3]=20+30=50,graph[0][5]=60+30=90)。变换为下面矩阵,然后继续找第一行除V0,V2,V4外的最小值min为50,得到下一个点Next=3,S={0,2,4,3},路径为:V0->V4->V3

 

 

V0

V1

V2

V3

V4

V5

V0

10000

10000

10

50

30

90

V1

10000

10000

5

10000

10000

10000

V2

10000

10000

10000

50

10000

10000

V3

10000

10000

10000

10000

20

10

V4

10000

10000

10000

10000

10000

60

V5

10000

10000

10000

10000

10000

10000

 

(4)将V3行的值加上min=50,与第一行除V0,V2,V4,V3内的元素值对比,若小于,则替换.

 

(替换了graph[0][5]=graph[3][5]+50=10+50=60)。变换为下面矩阵,然后继续找第一行除V0,V2,V4V3外的最小值min为60,得到下一个点Next=5,S={0,2,4,3,5},路径为:V0->V4->V3->V5

 

 

V0

V1

V2

V3

V4

V5

V0

10000

10000

10

50

30

60

V1

10000

10000

5

10000

10000

10000

V2

10000

10000

10000

50

10000

10000

V3

10000

10000

10000

10000

20

10

V4

10000

10000

10000

10000

10000

60

V5

10000

10000

10000

10000

10000

10000

(5)将V5行的值加上min=50,与第一行除V0,V2,V4,V3,V5内的元素值对比,若小于,则替换.

 

(没有替换)。矩阵不变,循环5次结束。

大家走过看过,给个赞吧撒~长得帅的都有这习惯呢·~~

4.4 运行结果图如下:

 

五 算法应用

 

源点到各点的最短距离:

后面完善

完整代码:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace shortestpath
{
    class Program
    {
        static int[,] graph = new int[6, 6] { { 10000, 10000, 10, 10000, 30, 100 }, { 10000, 10000, 5, 10000, 10000, 10000 }, { 10000, 10000, 10000, 50, 10000, 10000 }, { 10000, 10000, 10000, 10000, 10000, 10 }, { 10000, 10000, 10000, 20, 10000, 60 }, { 10000, 10000, 10000, 10000, 10000, 10000 } };
        static int[] S = new int[6] { 0, 0, 0, 0, 0, 0 };//最短路径的顶点集合
      static string[] mid = new string[6]{"","","","","",""};//点的路线
      public static int IsContain(int m)//判断元素是否在mst中
      {
          int index = -1;
          for (int i = 1; i < 6; i++)
          {
              if (S[i] == m)
              {
                  index = i;
              }
          }
          return index;
      }
        /// <summary>
        /// Dijkstrah实现最短路算法
        /// </summary>
        static void ShortestPathByDijkstra()
        {
            int min;
            int next;
            
            for (int f = 5; f > 0;f--)
            {
                //置为初始值
               
                 min = 1000;
                 next = 0;//第一行最小的元素所在的列 next点
                //找出第一行最小的列值
                for (int j = 1; j < 6; j++)//循环第0行的列
                {
                    if ((IsContain(j) == -1) && (graph[0, j] < min))//不在S中,找出第一行最小的元素所在的列
                    {
                        min = graph[0, j];
                        next = j;
                    }
                }
                //将下一个点加入S
                S[next] = next;
                //输出最短距离和路径
                if (min == 1000)
                {
                    Console.WriteLine("V0到V{0}的最短路径为:无", next);
                }
                else
                {
                    Console.WriteLine("V0到V{0}的最短路径为:{1},路径为:V0{2}->V{0}", next, min, mid[next]);
                }
                // 重新初始0行所有列值
                for (int j = 1; j < 6; j++)//循环第0行的列
                {
                    if (IsContain(j) == -1)//初始化除包含在S中的
                    {
                        if ((graph[next, j] + min) < graph[0, j])//如果小于原来的值就替换
                        {
                            graph[0, j] = graph[next, j] + min;
                            mid[j] = mid[next] + "->V" + next;//记录过程点
                        }
                    }
                }

            }
          
        }

       
        static void Main(string[] args)
        {
            ShortestPathByDijkstra();
           
        }
    }
}

 

 

 

 

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值