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算法的思路其实很朴实,就是如果两棵树的一条割边是所有连接这两棵树的割边中最小的边,那么这条边一定在这两棵树组成的图形中跨树最短路径中。通过不断的合并树最终形成一棵完整的最短路径树。当然,合并的法则是按边权从小到大进行。