单源最短路径算法(Dijkstra算法)
2012-08-01 21:09 java线程例子 阅读(311) 评论(0) 收藏 举报
1、图形相关类(前面有个,但这里增加了一些属性)
Dijkstra算法的思路其实很朴实,就是如果两棵树的一条割边是所有连接这两棵树的割边中最小的边,那么这条边一定在这两棵树组成的图形中跨树最短路径中。通过不断的合并树最终形成一棵完整的最短路径树。当然,合并的法则是按边权从小到大进行。
/// <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算法的思路其实很朴实,就是如果两棵树的一条割边是所有连接这两棵树的割边中最小的边,那么这条边一定在这两棵树组成的图形中跨树最短路径中。通过不断的合并树最终形成一棵完整的最短路径树。当然,合并的法则是按边权从小到大进行。