算法导论看到一半,觉得还是有必要继续看,边看边试试,下面是最小生成树的Kruskal算法,这个算法原理看起来很复杂,但实现起来很简单:开始的时候是每个顶点一棵树,并将边按权重升序排列。然后从前到后按循序选边,如果当前选择的边的两个顶点分在两棵不同的树中,则将该边加入到最小生成树中,并合当前边连接的两棵树,如果边得两个顶点在相同的树中,则不做任何处理,需要注意的是这个算法是针对无向连通图的,如果是有限图,则需要在算法中做些处理,但算法原理是一样的。看代码:
1、树和图相关类
/// <summary>
/// 图类,由节点和边构成.
/// </summary>
public class Graphic
{
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);
}
}
}
/// <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(string Symbol)
{
this.Symbol = Symbol;
}
}
/// <summary>
/// 边类,包括两个节点和权重.
/// </summary>
public class Edge
{
public Node Node1 { get; set; }
public Node Node2 { get; set; }
public int Weight { get; set; }
public Edge(Node N1, Node N2, int Weight)
{
this.Node1 = N1;
this.Node2 = N2;
this.Weight = Weight;
}
}
2、算法类
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);
}
}
这个算法的时间复杂度:O(E*lgV).时间除了初始化外,主要用在查找节点所在树和合并树上。