Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minimum spanning tree algorithm #22

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/graaflib/algorithm/minimum_spanning_tree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma once

#include <graaflib/graph.h>
#include <graaflib/types.h>

#include <algorithm>
#include <utility>
#include <vector>

namespace graaf::algorithm {

// Helper struct for representing disjoint sets
struct DisjointSet {
Comment on lines +12 to +13
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we put this in a namespace detail? I think it can also be moved to minimum_spanning_tree.tpp to keep the interface in the header file as clean as possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I have done

std::vector<vertex_id_t> parent;
std::vector<size_t> rank;

DisjointSet(size_t num_vertices) {
parent.resize(num_vertices);
rank.resize(num_vertices, 0);
for (vertex_id_t i = 0; i < num_vertices; ++i) parent[i] = i;
}

vertex_id_t find(vertex_id_t vertex) {
if (vertex != parent[vertex]) parent[vertex] = find(parent[vertex]);
return parent[vertex];
}

void union_sets(vertex_id_t vertex1, vertex_id_t vertex2) {
vertex_id_t root1 = find(vertex1);
vertex_id_t root2 = find(vertex2);
if (root1 != root2) {
if (rank[root1] < rank[root2]) std::swap(root1, root2);
parent[root2] = root1;
if (rank[root1] == rank[root2]) ++rank[root1];
}
}
};

template <typename V, typename E, graph_spec S>
std::vector<std::pair<vertex_id_t, vertex_id_t>> minimum_spanning_tree(
const graph<V, E, S>& graph) {
using Edge = std::pair<edge_weight_t, std::pair<vertex_id_t, vertex_id_t>>;
Comment on lines +38 to +42
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, could you move the implementation to the .tpp file to keep you interface clean here? It would also be nice to have a short docstring on what this function does.

Maybe you could take a look at algorithm/graph_traversal.h for inspiration.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done sir

std::vector<Edge> edges;

// Populate the edges vector with all edges in the graph
for (vertex_id_t source = 0; source < graph.num_vertices(); ++source) {
for (auto edge : graph.get_outgoing_edges(source)) {
vertex_id_t target = edge.target;
edge_weight_t weight = edge.weight;
edges.push_back({weight, {source, target}});
}
}

// Sort the edges in ascending order based on their weights
std::sort(edges.begin(), edges.end());

// Initialize the minimum spanning tree and the disjoint set
std::vector<std::pair<vertex_id_t, vertex_id_t>> minimum_spanning_tree;
DisjointSet disjoint_set(graph.num_vertices());

// Perform Kruskal's algorithm
for (const auto& edge : edges) {
vertex_id_t source = edge.second.first;
vertex_id_t target = edge.second.second;

if (disjoint_set.find(source) != disjoint_set.find(target)) {
disjoint_set.union_sets(source, target);
minimum_spanning_tree.push_back({source, target});
}
}

return minimum_spanning_tree;
}

} // namespace graaf::algorithm
8 changes: 8 additions & 0 deletions src/graaflib/algorithm/minimum_spanning_tree.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "minimum_spanning_tree.h"

namespace graaf::algorithm {

// No additional implementation is required for this example
// as the entire implementation is already provided in the header file.

} // namespace graaf::algorithm
70 changes: 70 additions & 0 deletions test/graaflib/algorithm/minimum_spanning_tree_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <cassert>
#include <iostream>

#include "minimum_spanning_tree.h"
Comment on lines +3 to +4
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "minimum_spanning_tree.h"
#include <graaflib/algorithm/minimum_spanning_tree.h>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include <graaflib/algorithm/minimum_spanning_tree.h>

Getting this error again : - graaflib/algorithm/minimum_spanning_tree.h: No such file or directory


void testMinimumSpanningTree() {
// Test Case 1: Empty graph
{
graaf::graph<int, int> graph;
auto result = graaf::algorithm::minimum_spanning_tree(graph);
assert(result.empty());
}

// Test Case 2: Single vertex
{
graaf::graph<int, int> graph;
graph.add_vertex(0);
auto result = graaf::algorithm::minimum_spanning_tree(graph);
assert(result.empty());
}

// Test Case 3: Linear graph
{
graaf::graph<int, int> graph;
graph.add_vertex(0);
graph.add_vertex(1);
graph.add_vertex(2);
graph.add_vertex(3);
graph.add_edge(0, 1, 2);
graph.add_edge(1, 2, 3);
graph.add_edge(2, 3, 4);
auto result = graaf::algorithm::minimum_spanning_tree(graph);
std::vector<std::pair<graaf::vertex_id_t, graaf::vertex_id_t>> expected{
{0, 1}, {1, 2}, {2, 3}};
assert(result == expected);
}

// Test Case 4: Complete graph
{
graaf::graph<int, int> graph;
graph.add_vertex(0);
graph.add_vertex(1);
graph.add_vertex(2);
graph.add_edge(0, 1, 2);
graph.add_edge(0, 2, 1);
graph.add_edge(1, 2, 3);
auto result = graaf::algorithm::minimum_spanning_tree(graph);
std::vector<std::pair<graaf::vertex_id_t, graaf::vertex_id_t>> expected{
{0, 2}, {0, 1}};
assert(result == expected);
}

// Test Case 5: Graph with disconnected components
{
graaf::graph<int, int> graph;
graph.add_vertex(0);
graph.add_vertex(1);
graph.add_vertex(2);
graph.add_vertex(3);
graph.add_edge(0, 1, 2);
graph.add_edge(2, 3, 3);
auto result = graaf::algorithm::minimum_spanning_tree(graph);
std::vector<std::pair<graaf::vertex_id_t, graaf::vertex_id_t>> expected{
{0, 1}, {2, 3}};
assert(result == expected);
}

// If no assert fails print -
std::cout << "All tests passed!\n";
}