前言 :
因为之前刚写过最小生成树算法,刚开始看到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,V4,V3外的最小值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();
}
}
}