最近学习Dijkstra算法和C#,用C#写了一个Dijkstra算法的演示程序。图中的节点要求使用26个字母来表示。节点之间的距离为整数,不相邻节点输入”I”或”i”来代表Infinite。该程序移除了输入的冗余,并且显示输入节点和距离后的邻接矩阵。同时会在最终结果给出前,打印出 Dijkstra算法的计算过程表和最短路径点的次序(path数组)。在代码中给出了详细的注释,对于学习Dijkstra算法的人具有一定帮助。
运行效果图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | 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(); } } } |