diff --git a/src/graph/Makefile b/src/graph/Makefile index 98890ac..9bc0a2c 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.c b/src/graph/graph.c index 0dd62db..649d2ae 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); @@ -202,17 +259,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 +288,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 d2af888..217889e 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. @@ -239,6 +246,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. * @@ -258,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 0000000..579d2c2 --- /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/tarjan.c b/src/graph/tarjan.c index 918d637..df53c0c 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 a535dc5..fe862fc 100644 --- a/src/graph/test.c +++ b/src/graph/test.c @@ -197,6 +197,70 @@ 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 "); + + 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)); @@ -325,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] @@ -345,8 +430,11 @@ 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); + test_graph_edges_ordered(); + test_graph_kruskal(); 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 60d8f58..6fc8d48 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/Makefile b/src/set/Makefile index d536de3..3f36180 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 0000000..7097104 --- /dev/null +++ b/src/set/set-disjoint.c @@ -0,0 +1,55 @@ +#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_free(DisjointSet *ds) { + free(ds->parent); + free(ds->rank); + free(ds); +} + +// 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 0000000..52e4050 --- /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 Free a disjoint-set data structure. + * + * @param[in,out] ds A pointer to a pointer to the disjoint-set. + */ +void set_disjoint_free(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/set.c b/src/set/set.c index c7920b5..45740b2 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 688c548..a469f59 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 diff --git a/src/set/test.c b/src/set/test.c index 704fe6c..5d92d5e 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,59 @@ 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_free(ds); +} + + int main(void) { printf("== Tests over Set data stucture"); test_set_contains(); @@ -143,5 +197,6 @@ int main(void) { test_set_union(); test_set_difference(); test_set_iterator(); + test_set_disjoint(); return 0; } diff --git a/src/stack/stack-dynamic.c b/src/stack/stack-dynamic.c index b049a7e..6b95308 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 0469e12..83a370b 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 700e7c6..bf9b038 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. *