首先新建一個網圖如下:
圖的表示法有好多中,最常用的應該是鄰接矩陣與鄰接表。上面的圖,邊很少,用鄰接表來表示就很不錯。
對於以上圖,可以對象出3個類。圖、節點、邊。3個實體類代碼如下:
邊Edge:
public class Edge { public string StartNodeID { get; set; } public string EndNodeID { get; set; } public int Weight { get; set; } }
節點Node:
public class Node { private string id; private IList<Edge> edgeList; public Node(string nid) { id = nid; edgeList = new List<Edge>(); } public string Id { get { return id; } } public IList<Edge> EdgeList { get { return edgeList; } } }
圖Graph:
public class Graph { public List<Node> NodeList = new List<Node>(); }
由於要求的就是最短路徑,路徑對象模擬如下:
public class Path { public string CurrentNodeId; public bool IsProcessed = false; public int Weight = 99999999; public List<string> PathNodeList = new List<string>(); }
最短路徑計算類:
/// <summary> /// 計算最短路徑幫助類 /// </summary> public class CaculateHelper { private Dictionary<string, Path> dicPath = new Dictionary<string, Path>(); public Dictionary<string, Path> DicPath { get { return dicPath; } } public void IniFirstNode(Graph graph, string StartNodeId) { Node OriginNode = null; foreach (Node node in graph.NodeList) { if (node.Id == StartNodeId) { OriginNode = node; } else { Path path = new Path(); path.CurrentNodeId = node.Id; dicPath.Add(path.CurrentNodeId, path); //初始化A->到所有邊都是默認的Path 99999999 } } //如果有A直接進入的邊,則設置為相應權重值,和記錄下路徑 foreach (Edge edge in OriginNode.EdgeList) { Path path = new Path(); path.CurrentNodeId = edge.EndNodeID; path.Weight = edge.Weight; path.PathNodeList.Add(edge.StartNodeID); dicPath[path.CurrentNodeId] = path; } } public Node GetFromNodeMinWeightNode(Graph graph) { Node CNode = null; KeyValuePair<string, Path> KVPPath = dicPath.Where(m => !m.Value.IsProcessed).OrderBy(m => m.Value.Weight).FirstOrDefault(); if (KVPPath.Key != null) { CNode = graph.NodeList.FirstOrDefault(m => m.Id == KVPPath.Value.CurrentNodeId); } return CNode; } /// <summary> /// 計算最短權值路徑 /// </summary> public void CatelateMinWeightRoad(Graph graph) { //取從第一個點出發,最小權值且未被訪問果的節點的點 Node CNode = GetFromNodeMinWeightNode(graph); //這段代碼是核心 循環每個頂點,看看經過該頂點是否會讓權值變小,如果會則存起此路徑。直到再未訪問過的點 while (CNode != null) { Path CurrentPath = dicPath[CNode.Id]; foreach (Edge edge in CNode.EdgeList) { Path TargetPath = dicPath[edge.EndNodeID]; int tempWeight = CurrentPath.Weight + edge.Weight; if (tempWeight < TargetPath.Weight) { TargetPath.Weight = tempWeight; TargetPath.PathNodeList.Clear(); for (int i = 0; i < CurrentPath.PathNodeList.Count; i++) { TargetPath.PathNodeList.Add(CurrentPath.PathNodeList[i].ToString()); } TargetPath.PathNodeList.Add(CNode.Id); } } //標志為已處理 dicPath[CNode.Id].IsProcessed = true; //再次獲取權值最小的點 CNode = GetFromNodeMinWeightNode(graph); } } }
主控制台程序:
class Program { static void Main(string[] args) { Graph graph = new Graph(); #region 初始化一個圖的 節點和邊 //***************** B Node ******************* Node aNode = new Node("A"); graph.NodeList.Add(aNode); //A -> B Edge aEdge1 = new Edge(); aEdge1.StartNodeID = aNode.Id; aEdge1.EndNodeID = "B"; aEdge1.Weight = 10; aNode.EdgeList.Add(aEdge1); //A -> C Edge aEdge2 = new Edge(); aEdge2.StartNodeID = aNode.Id; aEdge2.EndNodeID = "C"; aEdge2.Weight = 20; aNode.EdgeList.Add(aEdge2); //A -> E Edge aEdge3 = new Edge(); aEdge3.StartNodeID = aNode.Id; aEdge3.EndNodeID = "E"; aEdge3.Weight = 30; aNode.EdgeList.Add(aEdge3); //***************** B Node ******************* Node bNode = new Node("B"); graph.NodeList.Add(bNode); //B -> C Edge bEdge1 = new Edge(); bEdge1.StartNodeID = bNode.Id; bEdge1.EndNodeID = "C"; bEdge1.Weight = 5; bNode.EdgeList.Add(bEdge1); //B -> E Edge bEdge2 = new Edge(); bEdge2.StartNodeID = bNode.Id; bEdge2.EndNodeID = "E"; bEdge2.Weight = 10; bNode.EdgeList.Add(bEdge2); //***************** C Node ******************* Node cNode = new Node("C"); graph.NodeList.Add(cNode); //C -> D Edge cEdge1 = new Edge(); cEdge1.StartNodeID = cNode.Id; cEdge1.EndNodeID = "D"; cEdge1.Weight = 30; cNode.EdgeList.Add(cEdge1); //***************** D Node ******************* Node dNode = new Node("D"); graph.NodeList.Add(dNode); //***************** C Node ******************* Node eNode = new Node("E"); graph.NodeList.Add(eNode); //E -> D Edge eEdge1 = new Edge(); eEdge1.StartNodeID = eNode.Id; eEdge1.EndNodeID = "D"; eEdge1.Weight = 20; eNode.EdgeList.Add(eEdge1); //E -> F Edge eEdge2 = new Edge(); eEdge2.StartNodeID = eNode.Id; eEdge2.EndNodeID = "F"; eEdge2.Weight = 20; eNode.EdgeList.Add(eEdge2); //***************** F Node ******************* Node fNode = new Node("F"); graph.NodeList.Add(fNode); #endregion //計算從A -> C的最短權值路線 string StartNodeId = "A"; string EndNodeId = "F"; CaculateHelper CH = new CaculateHelper(); //第一步,初始化初始化源點 A 到 其他各點的 權重以及 路徑(完成 A->B A->C A->E A->D 邊權重,與A無直接邊的則默認99999999) CH.IniFirstNode(graph, StartNodeId); //第二步,從權重最小的點開始,一直到權值最大的點 CH.CatelateMinWeightRoad(graph); #region 以下與計算無關,僅僅用於將結果打印出來 Path ShowPath = CH.DicPath[EndNodeId]; foreach(string MiddleNodeId in ShowPath.PathNodeList) { Console.WriteLine(MiddleNodeId); } Console.WriteLine(ShowPath.Weight); #endregion Console.ReadKey(); } }
不要小看上面這幾行代碼,哥看了好久才看懂,如果Node里加幾個坐標,就能在地圖上面展示了。下一篇會將它改造成可以在地圖上面展示的路徑規划。