From d6f670b3909a2c484f3ceada0081d4163a33495c Mon Sep 17 00:00:00 2001 From: Manoel Vilela Date: Mon, 17 Nov 2025 02:39:23 -0300 Subject: [PATCH 1/4] feat: add graph_strong_components based on tarjan --- src/graph/graph.c | 12 +++--- src/graph/graph.h | 8 ++++ src/graph/tarjan.c | 88 ++++++++++++++++++++++++++++++--------- src/graph/test.c | 37 ++++++++++++++++ src/stack/stack-dynamic.c | 4 ++ src/stack/stack-static.c | 9 ++++ src/stack/stack.h | 10 +++++ 7 files changed, 143 insertions(+), 25 deletions(-) diff --git a/src/graph/graph.c b/src/graph/graph.c index 0dd62dbd..e0cd539e 100644 --- a/src/graph/graph.c +++ b/src/graph/graph.c @@ -202,17 +202,17 @@ void graph_print(Graph *g) { int u = *(int*)iterator_next(it); Set *neighbors = (Set*) hash_table_gen_get(g->adj, u, NULL); if (g->tarjan) { - Iterator *it = set_iterator_items(neighbors); + Iterator *neighbors_it = set_iterator_items(neighbors); printf("{"); - while (!iterator_done(it)) { - List *list = (List*) iterator_next(it); + while (!iterator_done(neighbors_it)) { + List *list = (List*) iterator_next(neighbors_it); printf("%d:%s", list->key, graph_edge_type_name((EdgeType)list->data)); - if (!iterator_done(it)) { + if (!iterator_done(neighbors_it)) { printf(", "); } } printf("}\n"); - iterator_free(it); + iterator_free(neighbors_it); } else if (g->weighted) { set_print_items(neighbors); } else { @@ -231,8 +231,8 @@ void graph_free(Graph *g) { void graph_export_to_dot(Graph *g, const char* filename) { FILE *fp = fopen(filename, "w"); if (fp == NULL) { - return; fprintf(stderr, "Could not open file %s for writing\n", filename); + return; } fprintf(fp, "%s G {\n", g->directed ? "digraph" : "graph"); diff --git a/src/graph/graph.h b/src/graph/graph.h index d2af8885..b673433c 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -239,6 +239,14 @@ bool graph_is_dag(Graph *g); */ Graph* graph_tarjan(Graph *g); +/** + * @brief Create a array of strong components using tarjan algorithm. + * @param g The graph to traverse. + * @return array of componentes indexed by node id. + * @ingroup DataStructureMethods + */ +int* graph_strong_components(Graph *g); + /** * This method is defined in acyclical.c because it inherits part of the acyclical code. * diff --git a/src/graph/tarjan.c b/src/graph/tarjan.c index 918d6376..df53c0c3 100644 --- a/src/graph/tarjan.c +++ b/src/graph/tarjan.c @@ -3,12 +3,22 @@ struct TarjanContext { Graph* g_tarjan; + int *components; + Stack* path; // for strong connected components int counter_exploration; int counter_complete; int *exploration; int *complete; }; +void free_tarjan_context(struct TarjanContext *tc) { + // keep on memor: g_tarjan + componentes + free(tc->exploration); + free(tc->complete); + stack_free(tc->path); + free(tc); +} + EdgeType tarjan_classify_edge(struct TarjanContext *tc, int parent, int u) { if (tc->exploration[u] == 0) { return TREE; @@ -21,13 +31,28 @@ EdgeType tarjan_classify_edge(struct TarjanContext *tc, int parent, int u) { } } +int update_components(struct TarjanContext *tc, EdgeType edge_type, int u, int v) { + if (edge_type == TREE) { + int tcu = tc->components[u]; + int tcv = tc->components[v]; + return tcu < tcv? tcu: tcv; + } else if (stack_has(tc->path, v)) { + int tcu = tc->components[u]; + int exv = tc->exploration[v]; + return tcu < exv? tcu: exv; + } + return tc->components[u]; +} + + static void graph_dfst( Graph *g, int node, struct TarjanContext *tc ) { tc->exploration[node] = ++tc->counter_exploration; - + tc->components[node] = tc->exploration[node]; + stack_push(tc->path, node); Set* neighbors_set = graph_get_neighbors(g, node); Iterator* set_it = set_iterator(neighbors_set); @@ -41,6 +66,13 @@ static void graph_dfst( if (edge_type == TREE) { graph_dfst(g, neighbor, tc); } + + tc->components[node] = update_components(tc, edge_type, node, neighbor); + } + + // pop elements for components visited in a cycle + if (tc->components[node] == tc->exploration[node]) { + while (stack_pop(tc->path) != node); } set_free(neighbors_set); iterator_free(set_it); @@ -48,36 +80,54 @@ static void graph_dfst( tc->complete[node] = ++tc->counter_complete; } - -Graph* graph_tarjan(Graph *g) { - struct TarjanContext tc; +struct TarjanContext* graph_tarjan_explore(Graph *g) { + struct TarjanContext *tc = (struct TarjanContext*) malloc(sizeof(struct TarjanContext)); int max_node_id = graph_max_node_id(g); - int *exploration = (int*) malloc(sizeof(int) * (max_node_id + 1)); - for (int i = 0; i <= max_node_id; i++) { + int n = max_node_id + 1; + int *exploration = (int*) malloc(n * sizeof(int)); + int *complete = (int*) malloc(n * sizeof(int)); + int *components= (int*) malloc(n * sizeof(int)); + + // initialize arrays + for (int i = 0; i < n; i++) { exploration[i] = 0; - } - int *complete = (int*) malloc(sizeof(int) * (max_node_id + 1)); - for (int i = 0; i <= max_node_id; i++) { complete[i] = 0; + components[i] = -1; } - tc.g_tarjan = graph_tarjan_create(graph_is_directed(g)); - tc.counter_complete = 0; - tc.counter_exploration = 0; - tc.exploration = exploration; - tc.complete = complete; + tc->g_tarjan = graph_tarjan_create(graph_is_directed(g)); + tc->counter_complete = 0; + tc->counter_exploration = 0; + tc->exploration = exploration; + tc->complete = complete; + tc->components = components; + tc->path = stack_create(); // dfs over each node Iterator *nodes = graph_nodes_iterator(g); while (!iterator_done(nodes)) { int node = *(int*) iterator_next(nodes); - if (tc.exploration[node] == 0) { - graph_dfst(g, node, &tc); + if (tc->exploration[node] == 0) { + graph_dfst(g, node, tc); } } iterator_free(nodes); - free(exploration); - free(complete); - return tc.g_tarjan; + return tc; +} + +Graph* graph_tarjan(Graph *g) { + struct TarjanContext *tc= graph_tarjan_explore(g); + Graph* g_tarjan = tc->g_tarjan; + free(tc->components); + free_tarjan_context(tc); + return g_tarjan; +} + +int* graph_strong_components(Graph *g) { + struct TarjanContext *tc= graph_tarjan_explore(g); + int* components = tc->components; + graph_free(tc->g_tarjan); + free_tarjan_context(tc); + return components; } diff --git a/src/graph/test.c b/src/graph/test.c index a535dc55..7ee8f58e 100644 --- a/src/graph/test.c +++ b/src/graph/test.c @@ -197,6 +197,42 @@ void test_graph_tarjan() { graph_free(g); } +void test_graph_strong_components() { + puts("== Graph strong components "); + + Graph *g = graph_create(); + + printf(":: input graph: \n"); + graph_add_edge(g, 1, 2); + graph_add_edge(g, 1, 3); + graph_add_edge(g, 2, 3); + graph_add_edge(g, 3, 4); + graph_add_edge(g, 4, 1); + graph_add_edge(g, 5, 1); + + graph_print(g); + + printf(":: strong components: \n"); + + int *components = graph_strong_components(g); + int n = graph_max_node_id(g) + 1; + for (int u = 0; u < n; u++) { + if (components[u] >= 0) { + printf("%d -> %d\n", u, components[u]); + } + } + + // expected: two components {1, 2, 3, 4} and {5} + assert(components[1] == components[2]); + assert(components[2] == components[3]); + assert(components[3] == components[4]); + assert(components[3] == components[4]); + assert(components[1] != components[5]); + + free(components); + graph_free(g); +} + void test_graph_export() { char cwd[PATH_MAX]; getcwd(cwd, sizeof(cwd)); @@ -345,6 +381,7 @@ int main(int argc, char *argv[]) { test_dfs(); test_graph_acyclical(); test_graph_tarjan(); + test_graph_strong_components(); test_graph_topological_sort(); test_graph_dijkstra(extra_tests); if (should_run_extra_tests(argc, argv)) { diff --git a/src/stack/stack-dynamic.c b/src/stack/stack-dynamic.c index b049a7e5..6b953082 100644 --- a/src/stack/stack-dynamic.c +++ b/src/stack/stack-dynamic.c @@ -40,6 +40,10 @@ int stack_pop(Stack *s) { return list_pop_head(&s->list); } +bool stack_has(Stack *s, int data) { + return list_search(s->list, data) != NULL; +} + void stack_print(Stack* s) { printf("list); diff --git a/src/stack/stack-static.c b/src/stack/stack-static.c index 0469e12f..83a370b8 100644 --- a/src/stack/stack-static.c +++ b/src/stack/stack-static.c @@ -50,6 +50,15 @@ void stack_print(Stack* s) { printf("]>"); } +bool stack_has(Stack *s, int data) { + for (int i = 0; i < s->n; i++) { + if (s->v[i] == data) { + return true; + } + } + return false; +} + void stack_println(Stack* s) { stack_print(s); printf("\n"); diff --git a/src/stack/stack.h b/src/stack/stack.h index 700e7c66..bf9b0380 100644 --- a/src/stack/stack.h +++ b/src/stack/stack.h @@ -56,6 +56,16 @@ void stack_push(Stack* s, int data); */ int stack_pop(Stack* s); +/** + * @brief Check if element is on the stack. + * + * @param s The stack to check. + * @param data The data to check on stack. + * @return true if element exists, false otherwise. + * @ingroup DataStructureMethods + */ +bool stack_has(Stack* s, int data); + /** * @brief Prints the elements of a stack to the console. * From 20df77f22aba9217ecffded65f6009a7e40b6604 Mon Sep 17 00:00:00 2001 From: Manoel Vilela Date: Mon, 17 Nov 2025 03:35:42 -0300 Subject: [PATCH 2/4] feat: add graph_edges_ordered --- src/graph/graph.c | 57 ++++++++++++++++++++++++++++++++++++++++++ src/graph/graph.h | 7 ++++++ src/graph/test.c | 29 +++++++++++++++++++++ src/list/single/list.c | 2 ++ src/set/set.c | 4 +++ src/set/set.h | 8 ++++++ 6 files changed, 107 insertions(+) diff --git a/src/graph/graph.c b/src/graph/graph.c index e0cd539e..649d2ae1 100644 --- a/src/graph/graph.c +++ b/src/graph/graph.c @@ -76,6 +76,63 @@ List* graph_edges(Graph *g) { return edges; } +int graph_edges_count(Graph *g) { + Iterator *nodes = graph_nodes_iterator(g); + int n_edges = 0; + while (!iterator_done(nodes)) { + int node = *(int*) iterator_next(nodes); + Set* neighbors = graph_get_neighbors(g, node); + n_edges += set_size(neighbors); + set_free(neighbors); + } + iterator_free(nodes); + return n_edges; +} + +// compare edges in descending order +int _compare_edge_weight(const void *a, const void *b) { + int x = *((const int*)a + 2); + int y = *((const int*)b + 2); + if (x < y) { + return 1; + } else if (x > y) { + return -1; + } else { + return 0; + } +} + +// return a list of edges ordered in ascending order +List* graph_edges_ordered(Graph *g) { + int edges_count = graph_edges_count(g); + int edges_by_weight[edges_count][3]; + List *edges = graph_edges(g); + Iterator *it = list_iterator(edges); + int rows = 0; + while (!iterator_done(it)) { + List *edge = (List*) iterator_next(it); + int u = edge->key; + int v = edge->data; + int w = graph_get_edge_weight(g, u, v); + edges_by_weight[rows][0] = u; + edges_by_weight[rows][1] = v; + edges_by_weight[rows][2] = w; + rows++; + } + iterator_free(it); + list_free(edges); + + qsort(edges_by_weight, edges_count, sizeof(int) * 3, _compare_edge_weight); + List *edges_ordered = list_create(); + for (int i = 0; i < edges_count; i++) { + int u = edges_by_weight[i][0]; + int v = edges_by_weight[i][1]; + edges_ordered = list_insert_with_key(edges_ordered, u, v); + } + + return edges_ordered; + +} size_t graph_size(Graph *g) { return hash_table_gen_size(g->adj); diff --git a/src/graph/graph.h b/src/graph/graph.h index b673433c..68982a3a 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -208,6 +208,13 @@ Iterator* graph_nodes_iterator(Graph *g); */ List* graph_edges(Graph *g); +/** + * @brief List with edges of the graph with (key,data) ordered ascending. + * @param g The graph to traverse. + * @ingroup DataStructureMethods + */ +List* graph_edges_ordered(Graph *g); + /** * @brief Get the maximum node id on the graph. * @return maximum node id on the graph. diff --git a/src/graph/test.c b/src/graph/test.c index 7ee8f58e..b9165f8d 100644 --- a/src/graph/test.c +++ b/src/graph/test.c @@ -197,6 +197,34 @@ void test_graph_tarjan() { graph_free(g); } +void test_graph_edges_ordered() { + Graph* g = graph_create(); + graph_add_edge_with_weight(g, 2, 4, 15); + graph_add_edge_with_weight(g, 1, 6, 14); + graph_add_edge_with_weight(g, 2, 3, 10); + graph_add_edge_with_weight(g, 1, 2, 7); + graph_add_edge_with_weight(g, 1, 3, 9); + printf(":: graph"); + graph_print(g); + + + List *expected = list_create(); + expected = list_append_with_key(expected, 1, 2); + expected = list_append_with_key(expected, 1, 3); + expected = list_append_with_key(expected, 2, 3); + expected = list_append_with_key(expected, 1, 6); + expected = list_append_with_key(expected, 2, 4); + + List *edges_ordered = graph_edges_ordered(g); + printf("Edges ordered: "); list_println(edges_ordered); + printf("Edges expected: "); list_println(expected); + assert(list_equal(edges_ordered, expected)); + + graph_free(g); + list_free(expected); + list_free(edges_ordered); +} + void test_graph_strong_components() { puts("== Graph strong components "); @@ -384,6 +412,7 @@ int main(int argc, char *argv[]) { test_graph_strong_components(); test_graph_topological_sort(); test_graph_dijkstra(extra_tests); + test_graph_edges_ordered(); if (should_run_extra_tests(argc, argv)) { test_graph_export(); } diff --git a/src/list/single/list.c b/src/list/single/list.c index 60d8f581..6fc8d484 100644 --- a/src/list/single/list.c +++ b/src/list/single/list.c @@ -472,6 +472,8 @@ List* list_from_iterator(Iterator *it) { return l; } + +// O(n^2): insertion sort void list_sort(List **list) { List* list_sorted = list_create(); Iterator* it = list_iterator_data(*list); diff --git a/src/set/set.c b/src/set/set.c index c7920b5c..45740b28 100644 --- a/src/set/set.c +++ b/src/set/set.c @@ -21,6 +21,10 @@ Set* set_create() { return set; } +int set_size(Set *set) { + return hash_table_size(set->memory); +} + Set* set_copy(Set *set) { Set *set_new = set_create(); hash_table_free(set_new->memory); diff --git a/src/set/set.h b/src/set/set.h index 688c548a..a469f594 100644 --- a/src/set/set.h +++ b/src/set/set.h @@ -41,6 +41,14 @@ Set* set_create(); */ Set* set_init(int set_size, ...); +/** + * @brief Get a the size of the set + * @param set to count + * @return number of elements + * @ingroup DataStructureMethods + */ +int set_size(Set *set); + /** * @brief Create a set as copy of another * @param set to copy From c7ad70d0337ca48b07a7f8bb733b4401faee3497 Mon Sep 17 00:00:00 2001 From: Manoel Vilela Date: Mon, 17 Nov 2025 04:10:09 -0300 Subject: [PATCH 3/4] feat(set): Implement Disjoint Set (Union-Find) data structure This commit introduces a new Disjoint Set (Union-Find) data structure to the module. - Added and implementing the Disjoint Set with , , (with path compression), and (with union by rank). - Updated to include the new source files and ensure proper compilation of dependencies. - Added comprehensive unit tests for the Disjoint Set functionality in . --- src/set/Makefile | 4 +-- src/set/set-disjoint.c | 61 ++++++++++++++++++++++++++++++++++++++++++ src/set/set-disjoint.h | 43 +++++++++++++++++++++++++++++ src/set/test.c | 56 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/set/set-disjoint.c create mode 100644 src/set/set-disjoint.h diff --git a/src/set/Makefile b/src/set/Makefile index d536de3c..3f36180c 100644 --- a/src/set/Makefile +++ b/src/set/Makefile @@ -14,7 +14,7 @@ INCLUDE := -I../ -L../list/single -L../hash-table/ # targets to compile TEST_TARGET = test -TARGETS = set.o +TARGETS = set.o set-disjoint.o LIBRARY_OBJS = $(TARGETS) TEST_BINARY = $(TEST_TARGET).$(EXTENSION) @@ -25,7 +25,7 @@ LIBRARY_TARGET = libset.a all: compile @echo > /dev/null -compile: $(TARGETS) $(TEST_TARGET).o $(MAIN_TARGET).o +compile: deps $(TARGETS) $(TEST_TARGET).o $(MAIN_TARGET).o deps: make clean library -C ../list/single CFLAGS=-DLIST_PRINT_KEY diff --git a/src/set/set-disjoint.c b/src/set/set-disjoint.c new file mode 100644 index 00000000..73b71edc --- /dev/null +++ b/src/set/set-disjoint.c @@ -0,0 +1,61 @@ +#include "set-disjoint.h" +#include "../utils/check_alloc.h" +#include + +struct DisjointSet { + int *parent; + int *rank; + int n; +}; + +DisjointSet *set_disjoint_create(int n) { + DisjointSet *ds = (DisjointSet *)malloc(sizeof(DisjointSet)); + check_alloc(ds); + ds->n = n; + ds->parent = (int *)malloc(sizeof(int) * n); + check_alloc(ds->parent); + ds->rank = (int *)malloc(sizeof(int) * n); + check_alloc(ds->rank); + for (int i = 0; i < n; i++) { + ds->parent[i] = i; + ds->rank[i] = 0; + } + return ds; +} + +void set_disjoint_destroy(DisjointSet **ds) { + if (ds == NULL || *ds == NULL) { + return; + } + free((*ds)->parent); + (*ds)->parent = NULL; + free((*ds)->rank); + (*ds)->rank = NULL; + free(*ds); + *ds = NULL; +} + +// Find with path compression +int set_disjoint_find(DisjointSet *ds, int i) { + if (ds->parent[i] != i) { + ds->parent[i] = set_disjoint_find(ds, ds->parent[i]); + } + return ds->parent[i]; +} + +// Union by rank +void set_disjoint_union(DisjointSet *ds, int i, int j) { + int root_i = set_disjoint_find(ds, i); + int root_j = set_disjoint_find(ds, j); + + if (root_i != root_j) { + if (ds->rank[root_i] < ds->rank[root_j]) { + ds->parent[root_i] = root_j; + } else if (ds->rank[root_i] > ds->rank[root_j]) { + ds->parent[root_j] = root_i; + } else { + ds->parent[root_j] = root_i; + ds->rank[root_i]++; + } + } +} diff --git a/src/set/set-disjoint.h b/src/set/set-disjoint.h new file mode 100644 index 00000000..4b231217 --- /dev/null +++ b/src/set/set-disjoint.h @@ -0,0 +1,43 @@ +#ifndef SET_DISJOINT_H +#define SET_DISJOINT_H + +/** + * @brief A disjoint-set data structure. + * @see https://en.wikipedia.org/wiki/Disjoint-set_data_structure + */ +typedef struct DisjointSet DisjointSet; + +/** + * @brief Creates a new disjoint-set data structure with n elements. + * + * @param[in] n The number of elements. + * @return A pointer to the new disjoint-set. + */ +DisjointSet *set_disjoint_create(int n); + +/** + * @brief Destroys a disjoint-set data structure. + * + * @param[in,out] ds A pointer to a pointer to the disjoint-set. + */ +void set_disjoint_destroy(DisjointSet **ds); + +/** + * @brief Finds the representative of the set containing element i. + * + * @param[in] ds The disjoint-set. + * @param[in] i The element to find. + * @return The representative of the set containing i. + */ +int set_disjoint_find(DisjointSet *ds, int i); + +/** + * @brief Merges the sets containing elements i and j. + * + * @param[in,out] ds The disjoint-set. + * @param[in] i The first element. + * @param[in] j The second element. + */ +void set_disjoint_union(DisjointSet *ds, int i, int j); + +#endif /* SET_DISJOINT_H */ diff --git a/src/set/test.c b/src/set/test.c index 704fe6cc..191f0c22 100644 --- a/src/set/test.c +++ b/src/set/test.c @@ -1,6 +1,7 @@ #include #include #include "set.h" +#include "set-disjoint.h" void test_set_contains() { printf("\n== test set_contains\n\n"); @@ -133,6 +134,60 @@ void test_set_iterator() { } +void test_set_disjoint() { + printf("\n== test set_disjoint\n\n"); + int n = 10; + DisjointSet *ds = set_disjoint_create(n); + + // Initially, each element is in its own set + for (int i = 0; i < n; i++) { + assert(set_disjoint_find(ds, i) == i); + } + + // Union some elements + set_disjoint_union(ds, 0, 1); + set_disjoint_union(ds, 2, 3); + set_disjoint_union(ds, 0, 2); // This should connect 0, 1, 2, 3 + + // Verify unions + assert(set_disjoint_find(ds, 0) == set_disjoint_find(ds, 1)); + assert(set_disjoint_find(ds, 0) == set_disjoint_find(ds, 2)); + assert(set_disjoint_find(ds, 0) == set_disjoint_find(ds, 3)); + assert(set_disjoint_find(ds, 1) == set_disjoint_find(ds, 2)); + assert(set_disjoint_find(ds, 1) == set_disjoint_find(ds, 3)); + assert(set_disjoint_find(ds, 2) == set_disjoint_find(ds, 3)); + + // Elements not involved in unions should still be in their own sets + assert(set_disjoint_find(ds, 4) == 4); + assert(set_disjoint_find(ds, 5) == 5); + + // Further unions + set_disjoint_union(ds, 4, 5); + set_disjoint_union(ds, 6, 7); + set_disjoint_union(ds, 8, 9); + set_disjoint_union(ds, 4, 6); // This should connect 4, 5, 6, 7 + + assert(set_disjoint_find(ds, 4) == set_disjoint_find(ds, 5)); + assert(set_disjoint_find(ds, 4) == set_disjoint_find(ds, 6)); + assert(set_disjoint_find(ds, 4) == set_disjoint_find(ds, 7)); + + // Union the two large sets + set_disjoint_union(ds, 0, 4); // Connects 0,1,2,3 with 4,5,6,7 + + assert(set_disjoint_find(ds, 0) == set_disjoint_find(ds, 4)); + assert(set_disjoint_find(ds, 1) == set_disjoint_find(ds, 5)); + assert(set_disjoint_find(ds, 2) == set_disjoint_find(ds, 6)); + assert(set_disjoint_find(ds, 3) == set_disjoint_find(ds, 7)); + + // Check that 8 and 9 are still separate + assert(set_disjoint_find(ds, 8) == set_disjoint_find(ds, 9)); + assert(set_disjoint_find(ds, 0) != set_disjoint_find(ds, 8)); + + set_disjoint_destroy(&ds); + assert(ds == NULL); +} + + int main(void) { printf("== Tests over Set data stucture"); test_set_contains(); @@ -143,5 +198,6 @@ int main(void) { test_set_union(); test_set_difference(); test_set_iterator(); + test_set_disjoint(); return 0; } From d809da73b76ce934e06b3992972759010307898e Mon Sep 17 00:00:00 2001 From: Manoel Vilela Date: Mon, 17 Nov 2025 04:34:18 -0300 Subject: [PATCH 4/4] feat: add graph_kruskal --- src/graph/Makefile | 2 +- src/graph/graph.h | 8 ++++++++ src/graph/kruskal.c | 24 ++++++++++++++++++++++++ src/graph/test.c | 22 ++++++++++++++++++++++ src/set/set-disjoint.c | 14 ++++---------- src/set/set-disjoint.h | 4 ++-- src/set/test.c | 3 +-- 7 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 src/graph/kruskal.c diff --git a/src/graph/Makefile b/src/graph/Makefile index 98890acc..9bc0a2c4 100644 --- a/src/graph/Makefile +++ b/src/graph/Makefile @@ -14,7 +14,7 @@ INCLUDE := -I../ -L../list/single -L../hash-table -L../set -L../queue -L../stac # targets to compile TEST_TARGET = test -TARGETS = graph.o bfs.o dfs.o acyclical.o tarjan.o dijkstra.o +TARGETS = graph.o bfs.o dfs.o acyclical.o tarjan.o dijkstra.o kruskal.o LIBRARY_OBJS = $(TARGETS) TEST_BINARY = $(TEST_TARGET).$(EXTENSION) diff --git a/src/graph/graph.h b/src/graph/graph.h index 68982a3a..217889e1 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -273,6 +273,14 @@ List* graph_topological_sort(Graph *g); */ Graph* graph_dijkstra(Graph* g, int source); +/** + * @brief Run kruskal algorithm to get the minimum-span tree. + * @param g The graph to traverse. + * @return a new graph with the minimum span tree. + * @ingroup DataStructureMethods + */ +Graph* graph_kruskal(Graph* g); + /** * @brief Run dijkstra algorithm and calculate the minimum distance. * @param g The graph to traverse. diff --git a/src/graph/kruskal.c b/src/graph/kruskal.c new file mode 100644 index 00000000..579d2c2e --- /dev/null +++ b/src/graph/kruskal.c @@ -0,0 +1,24 @@ +#include "graph.h" +#include "../set/set-disjoint.h" + +Graph* graph_kruskal(Graph *g) { + Graph *g_kruskal = graph_create(); + DisjointSet *components = set_disjoint_create(graph_max_node_id(g) + 1); + List *edges = graph_edges_ordered(g); + Iterator *it = list_iterator(edges); + + while (!iterator_done(it)) { + List *edge = (List*) iterator_next(it); + int u = edge->key; + int v = edge->data; + if (set_disjoint_find(components, u) != set_disjoint_find(components, v)) { + graph_add_edge_with_weight(g_kruskal, u, v, graph_get_edge_weight(g, u, v)); + set_disjoint_union(components, u, v); + } + } + + set_disjoint_free(components); + iterator_free(it); + list_free(edges); + return g_kruskal; +} diff --git a/src/graph/test.c b/src/graph/test.c index b9165f8d..fe862fcc 100644 --- a/src/graph/test.c +++ b/src/graph/test.c @@ -389,6 +389,27 @@ void test_graph_dijkstra(bool extra_tests) { graph_free(dijkstra_result); } +void test_graph_kruskal() { + puts("== Graph kruskal test"); + Graph *g = graph_create(); + graph_add_edge_with_weight(g, 1, 2, 10); + graph_add_edge_with_weight(g, 1, 3, 20); + graph_add_edge_with_weight(g, 2, 3, 5); + graph_add_edge_with_weight(g, 3, 4, 30); + graph_add_edge_with_weight(g, 4, 1, 9); + graph_add_edge_with_weight(g, 5, 1, 7); + + printf(":: input graph\n"); + graph_print(g); + + printf(":: kruskal tree\n"); + Graph *g_kruskal = graph_kruskal(g); + graph_print(g_kruskal); + + graph_free(g); + graph_free(g_kruskal); +} + bool should_run_extra_tests(int argc, char *argv[]) { // Iterate through the command-line arguments starting from argv[1] @@ -413,6 +434,7 @@ int main(int argc, char *argv[]) { test_graph_topological_sort(); test_graph_dijkstra(extra_tests); test_graph_edges_ordered(); + test_graph_kruskal(); if (should_run_extra_tests(argc, argv)) { test_graph_export(); } diff --git a/src/set/set-disjoint.c b/src/set/set-disjoint.c index 73b71edc..7097104b 100644 --- a/src/set/set-disjoint.c +++ b/src/set/set-disjoint.c @@ -23,16 +23,10 @@ DisjointSet *set_disjoint_create(int n) { return ds; } -void set_disjoint_destroy(DisjointSet **ds) { - if (ds == NULL || *ds == NULL) { - return; - } - free((*ds)->parent); - (*ds)->parent = NULL; - free((*ds)->rank); - (*ds)->rank = NULL; - free(*ds); - *ds = NULL; +void set_disjoint_free(DisjointSet *ds) { + free(ds->parent); + free(ds->rank); + free(ds); } // Find with path compression diff --git a/src/set/set-disjoint.h b/src/set/set-disjoint.h index 4b231217..52e40509 100644 --- a/src/set/set-disjoint.h +++ b/src/set/set-disjoint.h @@ -16,11 +16,11 @@ typedef struct DisjointSet DisjointSet; DisjointSet *set_disjoint_create(int n); /** - * @brief Destroys a disjoint-set data structure. + * @brief Free a disjoint-set data structure. * * @param[in,out] ds A pointer to a pointer to the disjoint-set. */ -void set_disjoint_destroy(DisjointSet **ds); +void set_disjoint_free(DisjointSet *ds); /** * @brief Finds the representative of the set containing element i. diff --git a/src/set/test.c b/src/set/test.c index 191f0c22..5d92d5e0 100644 --- a/src/set/test.c +++ b/src/set/test.c @@ -183,8 +183,7 @@ void test_set_disjoint() { assert(set_disjoint_find(ds, 8) == set_disjoint_find(ds, 9)); assert(set_disjoint_find(ds, 0) != set_disjoint_find(ds, 8)); - set_disjoint_destroy(&ds); - assert(ds == NULL); + set_disjoint_free(ds); }