八.迪杰斯特拉(Dijkstra)算法
文章目录
- 迪杰斯特拉算法
- 应用场景—最短路径问题
- 迪杰斯特拉(Dijkstra)算法介绍
- 迪杰斯特拉(Dijkstra)算法过程
- 思路图解
- 代码实现
迪杰斯特拉算法
应用场景—最短路径问题
看一个应用场景和问题
1)战争期间,胜利乡有7个村庄(A,B,C,D,E,F,G),现在有六个邮差,从G点出发,需要分别把邮件分别送到A,B,C,D,E,F六个村庄
2)各个村庄的距离用边线表示(权),比如A—B距离5公里
3)问:如何计算出G村庄到其他各个村庄的最短距离?
4)如果从其他点出发到各个点的最短距离又是多少?
迪杰斯特拉(Dijkstra)算法介绍
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止
迪杰斯特拉(Dijkstra)算法过程
设置出发顶点为v,顶点集合V{v1,v2,vi…},v到V中各顶点的距离构成距离集合Dis, Dis{d1,d2,di…} , Dis集合记录着v到图中各顶点的距离(到自身可以看作0,v到vi距离对应为di)
1)从Dis中选择值最小的di并移出Dis集合,同时移出V集合中对应的顶点vi,此时的v到vi即为最短路径
2)更新Dis集合,更新规则为:比较v到V集合中顶点的距离值,与v通过vi到V集合中顶点的距离值,保留值较小的一个(同时也应该更新顶点的前驱节点为vi,表明是通过vi到达的)
3)重复执行两步骤,直到最短路径顶点为目标顶点即可结束
思路图解
class VisitedVertex{ //已访问顶点集合
public int[] already_arr; //记录各个顶点是否访问过 1表示访问过,0未访问过,会动态更新
public int[] pre_visited; //每个下标对应的值为前一个顶点下标,会动态更新
public int[] dis; //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到其他顶点的距离,会动态更新,求的最短距离就会存放到dis
}
代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DijkstraAlgorithm
{class Program{static void Main(string[] args){char[] vertex = new char[] { 'A', 'B', 'C','D', 'E', 'F', 'G' }; //顶点数组//邻接矩阵int[,] matrix = new int[vertex.Length, vertex.Length]; //二维数组int N = 65535; //表示不可连接matrix = new int[,]{{N,5,7,N,N,N,2 },{5,N,N,9,N,N,3 },{7,N,N,N,8,N,N },{N,9,N,N,N,4,N },{N,N,8,N,N,5,4 },{N,N,N,4,5,N,6 },{2,3,N,N,4,6,N },};//创建一个图对象Graph graph = new Graph(vertex, matrix);//测试,看看图的邻接矩阵是否okgraph.showGraph();//测试迪杰斯特拉算法graph.dsj(6);graph.showDijkstra();}}class Graph //图{private char[] vertex; //顶点数组private int[,] matrix; //邻接矩阵private VisitedVertex vv; //已经访问的顶点的集合//构造器public Graph(char[] vertex,int[,] matrix){this.vertex = vertex;this.matrix = matrix;}//显示最后结果public void showDijkstra(){vv.show();}//显示图的方法public void showGraph(){for (int i = 0; i < vertex.Length; i++){for (int j = 0; j < vertex.Length; j++){Console.Write( "{0,7}",matrix[i,j]);}Console.WriteLine();}}//迪杰斯特拉算法/// <summary>/// /// </summary>/// <param name="index">表示出发顶点的下标</param>public void dsj(int index){vv = new VisitedVertex(vertex.Length, index);update(index); //更新index顶点到周围顶点的距离和前驱顶点//把第一个顶点走完,然后把剩余的顶点一个一个走一遍,每走一次选一个新的顶点for (int j = 1; j < vertex.Length; j++){index = vv.updateArr(); //选择并返回新的访问顶点update(index); //更新index顶点到周围顶点的距离和前驱顶点}}//更新index顶点到周围顶点的距离和周围顶点的前驱顶点private void update(int index){int len = 0;//遍历我们的邻接矩阵matrix[index]行,因为正在考虑index跟其他顶点的关系for (int j = 0; j < matrix.GetLength(0); j++) //GetLength(0)是行的长度,GetLength(1)是列的长度{//len含义:出发顶点到index顶点的距离+从index顶点到j顶点的距离len = vv.getDis(index)+matrix[index,j]; //如果j这个顶点没有被访问过,并且len小于出发顶点到j顶点的距离,就需要更新if(!vv.In(j) && len < vv.getDis(j)) //j没有被访问过,把j考虑进去,距离还变小了{vv.updatePre(j, index); //更新j顶点的前驱为index顶点vv.updateDis(j, len); //更新出发顶点到j顶点的距离}}}}//已访问顶点集合class VisitedVertex{public int[] already_arr; //记录各个顶点是否访问过 1表示访问过,0未访问过,会动态更新public int[] pre_visited; //每个下标对应的值为前一个顶点下标,会动态更新public int[] dis; //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到其他顶点的距离,会动态更新,求的最短距离就会存放到dis//构造器/// <summary>/// /// </summary>/// <param name="length">表示顶点的个数</param>/// <param name="index">出发顶点对应的下标,比如G顶点,下标就是6</param>public VisitedVertex(int length, int index){this.already_arr = new int[length];this.pre_visited = new int[length];this.dis = new int[length];//初始化dis数组dis = new int[] { 65535, 65535, 65535, 65535, 65535, 65535, 0 };this.already_arr[index] = 1; //设置出发顶点被访问过}/// <summary>/// 功能:判断index顶点是否被访问过/// </summary>/// <param name="index"></param>/// <returns>如果访问过返回true,否则返回false</returns>public Boolean In(int index){return already_arr[index] == 1; }/// <summary>/// 功能:更新出发顶点到index这个顶点的距离/// </summary>/// <param name="index">更新哪一个</param>/// <param name="len">更新为多少</param>public void updateDis(int index,int len) {dis[index] = len; }/// <summary>/// 功能:更新pre顶点的前驱顶点为index顶点/// </summary>/// <param name="pre"></param>/// <param name="index"></param>public void updatePre(int pre,int index){pre_visited[pre] = index;}/// <summary>/// 返回出发顶点到index顶点的距离/// </summary>public int getDis(int index){return dis[index];}//继续选择并返回新的访问顶点,比如这里的G完后,就是A点作为新的访问顶点(注意不是出发顶点)public int updateArr(){int min = 65535, index = 0;for (int i = 0; i < already_arr.Length; i++){if (already_arr[i]==0 && dis[i] < min){min = dis[i];index = i; //下一个访问顶点就变成i了}}//更新index顶点被访问过already_arr[index] = 1;return index;}//显示最后的结果,即将三个数组的情况输出public void show(){Console.WriteLine("===================================================");//输出already_arrforeach (var item in already_arr){Console.Write(item+" ");}Console.WriteLine();//输出前驱顶点pre_visitedforeach (var item in pre_visited){Console.Write(item+" ");}Console.WriteLine();//输出disforeach (var item in dis){Console.Write(item+" ");}Console.WriteLine();//为了好看最后的最短距离,我们处理char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };int count = 0;foreach (var i in dis){if (i != 65535){Console.Write(vertex[count]+"("+i+")"+" ");}else{Console.WriteLine("N");}count++;}Console.WriteLine();}}
}