/// <summary> /// Uses Prim's algorithm to build an MST spanning the mstNodes. /// O(|mstNodes|^2) runtime. /// </summary> /// <param name="startFrom">A GraphNode to start from.</param> public void Span(GraphNode startFrom) { var adjacentEdgeQueue = new LinkedListPriorityQueue <LinkedGraphEdge>(100); var startIndex = startFrom.DistancesIndex; // All nodes that are not yet included. var toAdd = new List <int>(_mstNodes.Count); // If the index node is already included. var inMst = new bool[_distances.CacheSize]; // The spanning edges. var mstEdges = new List <GraphEdge>(_mstNodes.Count); for (var i = 0; i < _mstNodes.Count; i++) { var index = _mstNodes[i].DistancesIndex; if (index != startIndex) { toAdd.Add(index); var adjacentEdge = new LinkedGraphEdge(startIndex, index); adjacentEdgeQueue.Enqueue(adjacentEdge, _distances[startIndex, index]); } } inMst[startIndex] = true; while (toAdd.Count > 0 && adjacentEdgeQueue.Count > 0) { int newIn; LinkedGraphEdge shortestEdge; // Dequeue and ignore edges that are already inside the MST. // Add the first one that is not. do { shortestEdge = adjacentEdgeQueue.Dequeue(); newIn = shortestEdge.Outside; } while (inMst[newIn]); mstEdges.Add(new GraphEdge( _distances.IndexToNode(shortestEdge.Inside), _distances.IndexToNode(shortestEdge.Outside))); inMst[newIn] = true; // Find all newly adjacent edges and enqueue them. for (var i = 0; i < toAdd.Count; i++) { var otherNode = toAdd[i]; if (otherNode == newIn) { toAdd.RemoveAt(i--); } else { var edge = new LinkedGraphEdge(newIn, otherNode); adjacentEdgeQueue.Enqueue(edge, _distances[newIn, otherNode]); } } } SpanningEdges = mstEdges; IsSpanned = true; }
public void TestMST() { // 0 -- 1 -- 2 -- 3 // \ | / // \ | / // 4 -- 5 -- 6 -- 7 bool[,] graph1 = { { false, true, false, false, true, false, false, false }, { true, false, true, false, false, false, false, false }, { false, true, false, true, false, true, false, false }, { false, false, true, false, false, true, false, false }, { true, false, false, false, false, true, false, false }, { false, false, true, true, true, false, true, false }, { false, false, false, false, false, true, false, true }, { false, false, false, false, false, false, true, false }, }; SearchGraph searchGraph1 = SearchGraphFromData(graph1); Dictionary<int, GraphNode> graphNodes1 = GetGraphNodesIdIndex(searchGraph1); var mstNodes1 = new List<GraphNode> { graphNodes1[3], graphNodes1[5], graphNodes1[7], graphNodes1[0] }; var distances = new DistanceLookup(mstNodes1); var mst1 = new MinimalSpanningTree(mstNodes1.Select(n => n.DistancesIndex).ToList(), distances); mst1.Span(graphNodes1[0].DistancesIndex); Assert.AreEqual(3, mst1.SpanningEdges.Count, "Wrong amount of spanning edges"); var goalEdges = new[] { new []{0, 5}, new []{5, 3}, new []{5, 7} }; foreach (var edge in goalEdges) { Assert.AreEqual(1, mst1.SpanningEdges.Select(e => new Tuple<ushort, ushort>(distances.IndexToNode(e.Inside).Id, distances.IndexToNode(e.Outside).Id)).Count( t => (t.Item1 == edge[0] && t.Item2 == edge[1]) || (t.Item1 == edge[1] && t.Item2 == edge[0])), "Edge " + edge + " not contained exactly once."); } }