最小生成树算法(Kruskal算法)
2012-06-24 16:38 java线程例子 阅读(352) 评论(0) 编辑 收藏 举报算法导论看到一半,觉得还是有必要继续看,边看边试试,下面是最小生成树的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).时间除了初始化外,主要用在查找节点所在树和合并树上。