Skip to content

Commit

Permalink
Add MST specialization algorithms
Browse files Browse the repository at this point in the history
Algorithms:
* Kruskal
* Prim

Changes to be committed:
	new file:   include/Algorithms/SpanningTree/Kruskal.hpp
	new file:   include/Algorithms/SpanningTree/Prim.hpp
  • Loading branch information
franziska-wegner committed Dec 5, 2023
1 parent ea8f04c commit 376d389
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 0 deletions.
88 changes: 88 additions & 0 deletions include/Algorithms/SpanningTree/Kruskal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Kruskal.hpp
*
* Created on: Nov 22, 2018
* Author: Franziska Wegner, Matthias Wolf
*/

#ifndef EGOA__ALGORITHMS__SPANNING_TREES__KRUSKAL_HPP
#define EGOA__ALGORITHMS__SPANNING_TREES__KRUSKAL_HPP

#include "Algorithms/SpanningTree/MST.hpp"

#include "DataStructures/Container/UnionFind.hpp"

namespace egoa {
/**
* @brief An implementation of Kruskal's algorithm for finding minimum spanning
* trees.
*
* @code{.cpp}
* Kuskal<TGraph> kruskal(graph, comparator);
* kruskal.Run();
* Subgraph<TGraph> spanningTree = kruskal.Result();
* @endcode
*
* @tparam GraphType The type of the graph.
*/
template<typename GraphType>
class Kruskal final : public MST<GraphType> {

using TSpanningTree = MST<GraphType>;
using typename TSpanningTree::TGraph;
using typename TSpanningTree::TEdge;
using typename TSpanningTree::TComparator;

public:
Kruskal(TGraph & graph,
TComparator comparator)
: TSpanningTree(graph, std::move(comparator))
{}

virtual ~Kruskal() {}

/**
* @brief Kruskal's Algorithm
* @details Kruskal's algorithm runs in O(|E| lg |V|) using binary
* heaps and calculates a MST. It uses techniques that are also
* common for connected component algorithms.
*
* Steps:
* 1. Increases the MST by exactly one edge in each iteration
* 2. It starts with |V| components
* 3. In each iteration the number of connected components shrinks by 1
* 4. To manage the connected components it uses a disjoint-set data structure
*
*/
virtual inline void Run() override {
UnionFind unionFind( this->Graph().NumberOfVertices() );

// Fill vector with edge identifiers
std::vector<Types::edgeId> edges;
edges.reserve(this->Graph().NumberOfEdges());
this->Graph().template for_all_edge_identifiers<ExecutionPolicy::sequential>([&edges](Types::edgeId id) {
edges.push_back(id);
});

// Sort the edges by their weights
std::sort( edges.begin(), edges.end(), this->Comparator());

std::vector<Types::edgeId> spanningTreeEdges;

for ( Types::edgeId edge : edges )
{
Types::vertexId source = this->Graph().EdgeAt( edge ).Source();
Types::vertexId target = this->Graph().EdgeAt( edge ).Target();
if ( !unionFind.InSameComponent( source, target ) ) {
unionFind.Union( source, target );
spanningTreeEdges.push_back( edge );
}
}

this->SetResult(std::move(spanningTreeEdges));
}
};

} // namespace egoa

#endif // EGOA__ALGORITHMS__SPANNING_TREES__KRUSKAL_HPP
113 changes: 113 additions & 0 deletions include/Algorithms/SpanningTree/Prim.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Prim.hpp
*
* Created on: Nov 22, 2018
* Author: Franziska Wegner, Matthias Wolf
*/

#ifndef EGOA__ALGORITHMS__SPANNING_TREES__PRIM_HPP
#define EGOA__ALGORITHMS__SPANNING_TREES__PRIM_HPP

#include "Algorithms/SpanningTree/MST.hpp"

#include "DataStructures/Container/Queues/MappingBinaryHeap.hpp"

namespace egoa {
/**
* @brief An implementation of Prim's algorithm for finding minimum spanning
* trees.
*
* @code{.cpp}
* Prim<TGraph> prim(graph, comparator);
* prim.Run();
* Subgraph<TGraph> spanningTree = prim.Result();
* @endcode
*
* @tparam GraphType The type of the graph.
* @tparam WeightType The type of the edge weights.
*/
template< typename GraphType>
class Prim final : public MST<GraphType> {

using TSpanningTree = MST<GraphType>;
using typename TSpanningTree::TGraph;
using typename TSpanningTree::TEdge;
using typename TSpanningTree::TComparator;

public:
Prim(TGraph & graph,
TComparator comparator)
: TSpanningTree( graph, std::move(comparator) )
{}

virtual ~Prim() {}

/**
* @brief Prim's algorithm
* @detail Prim's algorithm is quite similar to Dijkstra's
* algorithm. Prim runs in O(|E| log |V|) using binary heaps.
* While using Fibonacci heaps the running time is then in O(|E| +
* |V| log |V|). The latter is an improvement while |V| << |E|.
*
* Steps:
* 1. While not all vertices are in the MST component
* 2. Relax the incident edges to u if necessary
* 3. Choose the edge that has the minimum weight between
* the grown MST component and the non-MST component, i.e.,
* no cycle will be created
*
* @pre This algorithm assumes that the vertex identifiers all lie in
* the interval [0, NumberOfVertices() - 1].
*/
virtual inline void Run ( ) override {
Types::count const numberOfVertices = this->Graph().NumberOfVertices();

if (numberOfVertices == 0) return;

std::vector<bool> isVertexInMst( numberOfVertices, false );
std::vector<bool> visited(numberOfVertices, false);
std::vector<Types::edgeId> edgesInSpanningTree;

// TODO: Use vector instead of the default unordered_map
MappingBinaryHeap<Types::vertexId, Types::edgeId> heap(this->Comparator());

Types::vertexId currentVertex = 0;
visited[currentVertex] = true;

while (true) {
isVertexInMst[currentVertex] = true;

// Iterate over all incident edges
this->Graph().template for_all_edges_at<ExecutionPolicy::sequential>( currentVertex,
[&]( TEdge const & edge ) {
Types::vertexId neighbor = edge.Other( currentVertex );

// Ignore edges to vertices that have already been included in the MST
if (isVertexInMst[neighbor]) return;

if (!visited[neighbor])
{ // The neighbor has not been visited before
heap.Insert(neighbor, edge.Identifier());
visited[neighbor] = true;
} else if ( this->Comparator()( edge.Identifier(), heap.KeyOf(neighbor) ) )
{
// Better edge to neighbor has been found
heap.ChangeKey(neighbor, edge.Identifier());
}
});

if (heap.Empty()) break;

Types::edgeId parentEdge;
std::tie(currentVertex, parentEdge) = heap.DeleteTop();
edgesInSpanningTree.push_back(parentEdge);
} // while loop

this->SetResult(std::move(edgesInSpanningTree));
}
};

} // namespace egoa


#endif // EGOA__ALGORITHMS__SPANNING_TREES__PRIM_HPP

0 comments on commit 376d389

Please sign in to comment.