1、圖形相關類(前面有個,但這裡增加了一些屬性)
/// <summary> /// 圖類,由節點和邊構成. /// </summary> public class Graphic { /// <summary> /// 用於圖形訪問臨時公開變數 /// </summary> public int FinishOrder { get; set; } /// <summary> /// 用於圖形訪問臨時公開變數 /// </summary> public int EnterOrder { get; set; } public List<Node> Nodes { get; set; } public List<Edge> Edges { get; set; } public Graphic() { Nodes = new List<Node>(); Edges = new List<Edge>(); } public void Add(Node Node) { if (this.Nodes.IndexOf(Node) < 0) { this.Nodes.Add(Node); } } public void Add(Edge Edge) { if (this.Edges.IndexOf(Edge) < 0) { this.Edges.Add(Edge); } } public void ResetTempAdjNodes() { foreach (var theNode in this.Nodes) { theNode.ResetTempAdjNodes(); } } } /// <summary> /// 樹類,包括節點和邊構成 /// </summary> public class Tree { public List<Node> Nodes { get; set; } public List<Edge> Edges { get; set; } public Tree() { Nodes = new List<Node>(); Edges = new List<Edge>(); } public void Add(Node Node) { if (this.Nodes.IndexOf(Node) < 0) { this.Nodes.Add(Node); } } public void Add(Edge Edge) { if (this.Edges.IndexOf(Edge) < 0) { this.Edges.Add(Edge); } } } /// <summary> /// 節點類 /// </summary> public class Node { public string Symbol { get; set; } public Node Parent { get; set; } /// <summary> /// 用於演算法臨時存放,一般為key值. /// </summary> public double TempVal { get; set; } public int VisitedSign { get; set; } /// <summary> /// 用於演算法臨時存放 /// </summary> public Edge TempEdge { get; set; } /// <summary> /// 鄰接節點 /// </summary> public Dictionary<Node, Edge> AdjNodes; /// <summary> /// 臨時鄰接節點,用於計算中補破壞原圖結構. /// </summary> public Dictionary<Node, Edge> TempAdjNodes; public Node(string Symbol) { this.Symbol = Symbol; AdjNodes = new Dictionary<Node, Edge>(); TempAdjNodes = new Dictionary<Node, Edge>(); } /// <summary> /// 同步臨時鄰接節點集合值. /// </summary> public void ResetTempAdjNodes() { TempAdjNodes.Clear(); foreach (var theDictItem in AdjNodes) { TempAdjNodes.Add(theDictItem.Key, theDictItem.Value); theDictItem.Value.ResetTempWeight(); } } /// <summary> /// 用於深度搜尋標記 /// </summary> public int EnterTime { get; set; } /// <summary> /// 用於深度搜尋標記 /// </summary> public int FinishTime { get; set; } public int FinishOrder { get; set; } public int EnterOrder { get; set; } } /// <summary> /// 邊類,包括兩個節點和權重. /// </summary> public class Edge { public Node Node1 { get; set; } public Node Node2 { get; set; } public double Weight { get; set; } public double TempWeight { get; set; } public int EdgeType { get; set; } public Edge(Node N1, Node N2, double Weight) { this.Node1 = N1; this.Node2 = N2; this.Weight = Weight; } public void ResetTempWeight() { this.TempWeight = this.Weight; } }
2、單源路徑基本操作
public class SingleSourcePath { /// <summary> /// 單源路徑中初始化圖的計算設定值 /// </summary> /// <param name="g">要初始化的圖</param> public void InitializeGraphic(Graphic g,Node s) { //Node節點的TempVal屬性存放最小路徑估計值,Parent屬性存放其父節點. foreach (var theNode in g.Nodes) { theNode.TempVal = double.MaxValue; theNode.Parent = null; } s.Parent = null; s.TempVal = 0; } /// <summary> /// 單源路徑中初始化圖的計算設定值(矩陣標記法) /// </summary> /// <param name="Parents"></param> /// <param name="Distance"></param> /// <param name="n"></param> /// <param name="s"></param> public void InitializeGraphic(int[] Parents, double[] Distance,int n,int s) { for (int i = 0; i < n; i++) { Parents[i] = -1; Distance[i] = double.PositiveInfinity; } Distance[s] = 0; } /// <summary> /// 鬆弛技術 /// </summary> /// <param name="GraphicMatrix">圖鄰接矩陣</param> /// <param name="Parents">頂點父節點</param> /// <param name="Distance">源點到其它節點的距離</param> /// <param name="u">頂點</param> /// <param name="v">頂點</param> public void Relax(double[,] GraphicMatrix,int[] Parents,double[] Distance,int u,int v) { if (double.IsPositiveInfinity(Distance[u])==true) { return; } if (Distance[v] > Distance[u] + GraphicMatrix[u,v]) { Distance[v] = Distance[u] + GraphicMatrix[u, v]; Parents[v] = u; } } /// <summary> /// 鬆弛邊Edge的兩個節點node1,node2. /// </summary> /// <param name="node1"></param> /// <param name="node2"></param> /// <param name="weight"></param> public void Relax(Edge edge) { if (edge.Node1.TempVal == double.MaxValue) { return; } if (edge.Node2.TempVal > edge.Node1.TempVal + edge.Weight) { edge.Node2.TempVal = edge.Node1.TempVal + edge.Weight; edge.Node2.Parent = edge.Node1; } } }
3、Dijkstra演算法
public class KruskalAlg { public Tree MST_Kruskal(Graphic g) { //為每個頂點建立一顆樹,僅包含一個頂點,做初始化 List<Tree> theTrees = new List<Tree>(); foreach (var theNode in g.Nodes) { Tree theTree_Tmp = new Tree(); theTree_Tmp.Add(theNode); theTrees.Add(theTree_Tmp); } //對邊進行排序 var theEdgesQuery = from e in g.Edges orderby e.Weight select e; var theSortEdges = theEdgesQuery.ToArray(); //剛開始最小產生樹為空白. Tree theMST = new Tree(); //沒有採用foreach,以保證訪問按排序進行. for(int i=0;i<theSortEdges.Count();i++) { var theEdge = theSortEdges[i]; //找theEdge邊的兩個點各自所在的樹. Tree theTree1 = FindTreeByNode(theEdge.Node1, theTrees); Tree theTree2 = FindTreeByNode(theEdge.Node2, theTrees); //如果theEdge邊的兩個點各自所在的樹不相同,則將該邊選入最小產生樹, //同時需要將兩個樹進行合并。 if (theTree1 != theTree2) { theMST.Edges.Add(theEdge); theMST.Nodes.Add(theEdge.Node1); theMST.Nodes.Add(theEdge.Node2); UnionTreeInForest(theTree1, theTree2, theEdge, theTrees); } } return theMST; } /// <summary> /// 在森林中尋找某個節點所在的樹 /// </summary> /// <param name="Node">要找的節點</param> /// <param name="Forest">森林</param> /// <returns></returns> private Tree FindTreeByNode(Node Node, List<Tree> Forest) { foreach (var theTree in Forest) { foreach (var theNode in theTree.Nodes) { if (theNode.Symbol == Node.Symbol) { return theTree; } } } return null; } /// <summary> /// 在這個演算法中,邊是可以不用合并進來的. /// </summary> /// <param name="Tree1"></param> /// <param name="Tree2"></param> /// <param name="Edge"></param> /// <param name="Forest"></param> private void UnionTreeInForest(Tree Tree1, Tree Tree2, Edge Edge, List<Tree> Forest) { Tree1.Nodes.AddRange(Tree2.Nodes); Tree1.Edges.AddRange(Tree2.Edges); Tree1.Edges.Add(Edge); Forest.Remove(Tree2); } }
Dijkstra演算法的思路其實很樸實,就是如果兩棵樹的一條割邊是所有串連這兩棵樹的割邊中最小的邊,那麼這條邊一定在這兩棵樹組成的圖形中跨樹最短路徑中。通過不斷的合并樹最終形成一棵完整的最短路徑樹。當然,合并的法則是按邊權從小到大進行。