代码改变世界

单源最短路径算法(Dijkstra算法)

2012-08-01 21:09  java线程例子  阅读(302)  评论(0编辑  收藏  举报
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算法的思路其实很朴实,就是如果两棵树的一条割边是所有连接这两棵树的割边中最小的边,那么这条边一定在这两棵树组成的图形中跨树最短路径中。通过不断的合并树最终形成一棵完整的最短路径树。当然,合并的法则是按边权从小到大进行。