最近学习Dijkstra算法和C#,用C#写了一个Dijkstra算法的演示程序。图中的节点要求使用26个字母来表示。节点之间的距离为整数,不相邻节点输入”I”或”i”来代表Infinite。该程序移除了输入的冗余,并且显示输入节点和距离后的邻接矩阵。同时会在最终结果给出前,打印出 Dijkstra算法的计算过程表和最短路径点的次序(path数组)。在代码中给出了详细的注释,对于学习Dijkstra算法的人具有一定帮助。
运行效果图:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CsharpVersion
{
class Program
{
public class DEF
{
public const int MAX = 26;
public const int INF = 999999;
}
public struct Graph
{
public char[] vetx;//用于存储每个节点的名字
public int vetxNum;//拥有节点的数量
public int[,] matrix; //相邻矩阵
}
static public Graph genGraph()
{
string input;
int i, j;
//初始化图的结构体
Graph map;
map.vetx = new char[DEF.MAX];
map.vetxNum = 0;
map.matrix = new int[DEF.MAX, DEF.MAX];
Console.Write("Please enter the number of nodes:");
input = Console.ReadLine();
map.vetxNum = Convert.ToInt32(input);
//获取每一个节点的字母
for (i = 0; i < map.vetxNum; i++)
{
Console.Write("Please enter the letter of nodes:");
map.vetx[i] = Convert.ToChar(Console.ReadLine());
}
//创建邻接矩阵
for (i = 0; i < map.vetxNum; i++)
for (j = i + 1; j < map.vetxNum; j++) //只需输入半个矩阵
{
Console.Write("Please enter the distance from {0} to {1} (I stands for Infinte):", map.vetx[i], map.vetx[j]);
input = Console.ReadLine();
if (input.ToLower() == "i")
map.matrix[i, j] = DEF.INF;
else
map.matrix[i, j] = Convert.ToInt32(input);
}
//填补节点自己到自己的距离 = 0
for (i = 0; i < map.vetxNum; i++)
map.matrix[i, i] = 0;
//填补重复部分的矩阵
for (i = map.vetxNum - 1; i > 0; i--)
for (j = i - 1; j >= 0; j--)
map.matrix[i, j] = map.matrix[j, i];
//debug: 显示邻接矩阵
Console.WriteLine("*************START**************");
Console.WriteLine("The matrix of distance is:");
for (i = 0; i < map.vetxNum; i++)
{
for (j = 0; j < map.vetxNum; j++)
{
Console.Write("{0}", map.matrix[i, j]);
Console.Write("\t");
}
Console.Write("\r\n");
}
Console.Write("\r\n");
return map;
}
//Dijkstra algirthm
static void dijkstra(Graph map, char sNode)
{
int[,] dist = new int[map.vetxNum, map.vetxNum];//距离数组
bool[] flag = new bool[map.vetxNum]; //标记数组
int min, tmp, preBaseVal = 0, i, j, preMarkedIndex, vs = 0;
int[] path = new int[map.vetxNum]; //用于存储推演表中所有被选中的中继点
Stack pathStack = new Stack();
//找出起始点的编号
for (i = 0; i < map.vetxNum; i++)
if (map.vetx[i] == sNode)
vs = i;
//距离数组初始化
for (i = 0; i < map.vetxNum; i++)
{
dist[0, i] = DEF.INF; // map.matrix[vs, i];
flag[i] = false;//所有节点标志位均设为未标记
}
//初始化起始点数据
dist[0, vs] = 0;//起始点到自身的距离为0
preMarkedIndex = vs;
for (i = 0; i < map.vetxNum - 1; i++) //i代表第几行,
{
//在推演表中第一行找出最小数
min = DEF.INF;
for (j = 0; j < map.vetxNum; j++) //j代表第几列
{
if (min > dist[i, j] && flag[j] == false)
{
min = dist[i, j];
preMarkedIndex = j;
preBaseVal = min; //记录本轮找到的最小值
}
}
flag[preMarkedIndex] = true;//将找到的节点标记
path[i] = preMarkedIndex;//将找到的节点(次序)保存
//把已标记的值复制到推演表下一行
for (j = 0; j < map.vetxNum; j++)
{
if (flag[j] == true) //找到已标记的节点
dist[i + 1, j] = dist[i, j]; //把上一行中对应的已标记值复制到本行中
}
//求出当前行未被标记列(节点)的距离
for (j = 0; j < map.vetxNum; j++)
{
if (flag[j] == false) //找出未标记节点
{
if (map.matrix[preMarkedIndex, j] == DEF.INF) //前驱结点到本列所指节点不可直接到达,复制上一行数据
{
dist[i + 1, j] = dist[i, j];
continue; //继续去判断下一个节点
}
else //前驱结到当前列所指节点可以直接到达时
{
dist[i + 1, j] = (dist[i, j] < (preBaseVal + map.matrix[preMarkedIndex, j]) ? dist[i, j] : (preBaseVal + map.matrix[preMarkedIndex, j]));
}
}
}
}
for (j = 0; j < map.vetxNum; j++) //找出最后一个剩余的节点,并加入到路径数组中path[]
{
if (flag[j] == false) //最后剩余的节点尚未标记
path[map.vetxNum-1] = j;
}
//打印推演表
Console.Write("\r\n");
Console.WriteLine("The calculation table is:");
for (i = 0; i < map.vetxNum; i++)
{
for (j = 0; j < map.vetxNum; j++)
{
Console.Write("{0}", dist[i, j]);
Console.Write("\t");
}
Console.Write("\r\n");
}
//debug: path的内容
Console.Write("Path=");
for (i = 0; i < map.vetxNum; i++)
Console.Write("{0}", map.vetx[path[i]]);
Console.Write("\r\n\r\n");
//找出路径结果
for (i = 0; i < map.vetxNum; i++)
{
tmp = i; //tmp代表了第几列
pathStack.Clear();//清空路径栈
Console.Write("The shortest distance from {0} to {1} is {2}: ", map.vetx[vs], map.vetx[i], dist[map.vetxNum - 1, i]);
//找出从给定点出发到达每一个节点的路径
for (j = map.vetxNum - 1; j > 0; j--)
{
if (j == map.vetxNum - 1) //j代表了第几行
pathStack.Push(map.vetx[i]); //将目的地节点压入路径栈
if (dist[j, tmp] == dist[j - 1, tmp])
continue;
else
{
pathStack.Push(map.vetx[path[j - 1]]);//将前驱结点名压入路径栈
tmp = path[j - 1]; //为下一次循环定位列,移动到前驱结点所在的列
}
}
//打印出路径栈中的路径
foreach (char c in pathStack)
{
Console.Write("->{0}", c);
}
Console.Write("\r\n");
}
Console.WriteLine("***************END***************");
}
static void Main(string[] args)
{
char startPoint;
Graph map = genGraph();
Console.Write("Please enter which node starts:");
startPoint = Convert.ToChar(Console.Read());
dijkstra(map, startPoint);
Console.ReadKey();
}
}
}