diff --git a/src/graph/Makefile b/src/graph/Makefile index 9bc0a2c4..ba156b5f 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 kruskal.o +TARGETS = graph.o bfs.o dfs.o acyclical.o tarjan.o dijkstra.o kruskal.o prim.o LIBRARY_OBJS = $(TARGETS) TEST_BINARY = $(TEST_TARGET).$(EXTENSION) diff --git a/src/graph/graph.c b/src/graph/graph.c index 649d2ae1..1eb19b00 100644 --- a/src/graph/graph.c +++ b/src/graph/graph.c @@ -131,7 +131,38 @@ List* graph_edges_ordered(Graph *g) { } return edges_ordered; +} + +List* graph_remove_duplicated_edges(List* edges) { + List *node = edges; + while (node != NULL) { + int u = node->key; + int v = node->data; + edges = list_remove_by_key_data(edges, v, u); + node = node->next; + } + return edges; +} + +int graph_edges_sum(Graph *g) { + int s = 0; + List *edges = graph_edges(g); + + if (!g->directed) { + edges = graph_remove_duplicated_edges(edges); + } + + Iterator *it = list_iterator(edges); + while (!iterator_done(it)) { + List *node = (List*) iterator_next(it); + int u = node->key; + int v = node->data; + s += graph_get_edge_weight(g, u, v); + } + list_free(edges); + iterator_free(it); + return s; } size_t graph_size(Graph *g) { @@ -232,6 +263,12 @@ bool graph_has_edge(Graph *g, int u, int v) { return set_contains(set_u, v); } +bool graph_has_node(Graph *g, int u) { + bool exists; + hash_table_gen_get(g->adj, u, &exists); + return exists; +} + Set* graph_get_neighbors(Graph *g, int node) { bool exists; Set *neighbors = (Set*) hash_table_gen_get(g->adj, node, &exists); @@ -257,6 +294,7 @@ void graph_print(Graph *g) { Iterator *it = graph_nodes_iterator(g); while(!iterator_done(it)) { int u = *(int*)iterator_next(it); + printf("%d: ", u); Set *neighbors = (Set*) hash_table_gen_get(g->adj, u, NULL); if (g->tarjan) { Iterator *neighbors_it = set_iterator_items(neighbors); diff --git a/src/graph/graph.h b/src/graph/graph.h index 217889e1..46c3d785 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -145,6 +145,15 @@ void graph_remove_node(Graph *g, int node); */ bool graph_has_edge(Graph *g, int u, int v); +/** + * @brief Checks if a node exists in the graph. + * @param g The graph. + * @param u The node. + * @return True if the node exists, false otherwise. + * @ingroup DataStructureMethods + */ +bool graph_has_node(Graph *g, int u); + /** * @brief Gets the neighbors of a node. * @param g The graph. @@ -215,6 +224,13 @@ List* graph_edges(Graph *g); */ List* graph_edges_ordered(Graph *g); +/** + * @brief Sum of the edge weights. If undirected, calculate (u, v) == (v, u) only once. + * @param g The graph to traverse. + * @ingroup DataStructureMethods + */ +int graph_edges_sum(Graph *g); + /** * @brief Get the maximum node id on the graph. * @return maximum node id on the graph. @@ -274,13 +290,22 @@ List* graph_topological_sort(Graph *g); Graph* graph_dijkstra(Graph* g, int source); /** - * @brief Run kruskal algorithm to get the minimum-span tree. + * @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 Prim algorithm to get the minimum-span tree. + * @param g The graph to traverse. + * @param start Initial node to start. + * @return a new graph with the minimum span tree. + * @ingroup DataStructureMethods + */ +Graph* graph_prim(Graph* g, int start); + /** * @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 index 579d2c2e..f0b26b89 100644 --- a/src/graph/kruskal.c +++ b/src/graph/kruskal.c @@ -2,7 +2,7 @@ #include "../set/set-disjoint.h" Graph* graph_kruskal(Graph *g) { - Graph *g_kruskal = graph_create(); + Graph *g_kruskal = graph_undirected_create(); DisjointSet *components = set_disjoint_create(graph_max_node_id(g) + 1); List *edges = graph_edges_ordered(g); Iterator *it = list_iterator(edges); diff --git a/src/graph/prim.c b/src/graph/prim.c new file mode 100644 index 00000000..a92100b5 --- /dev/null +++ b/src/graph/prim.c @@ -0,0 +1,46 @@ +#include "graph.h" +#include "../utils/pair_hash.h" +#include "../pqueue/pqueue.h" + +void update_heap(Graph* g, PQueue* pq, Set* visited, int start) { + Set* neighbors = graph_get_neighbors(g, start); + Iterator *it = set_iterator_items(neighbors); + while (!iterator_done(it)) { + List *neighbor_weight = (List*) iterator_next(it); + if (!set_contains(visited, neighbor_weight->key)) { + int k = pair_hash(start, neighbor_weight->key); + int w = neighbor_weight->data; + pqueue_insert(pq, k, w); + } + } + set_free(neighbors); + iterator_free(it); +} + +// WARNING: this implementation only return the Minimum Spanning Tree +// of conected component from node. If some node is +// unreachable from will be not included in the final tree. +Graph* graph_prim(Graph *g, int start) { + Graph* g_prim = graph_undirected_create(); + Set* visited = set_create(); + PQueue *pq = pqueue_create(MIN_PQUEUE); + update_heap(g, pq, visited, start); + set_add(visited, start); + + while (!pqueue_is_empty(pq)) { + PQueueNode node = pqueue_extract(pq); + int u = unpair_hash_x(node.key); + int v = unpair_hash_y(node.key); + int w = node.value; + if (set_contains(visited, v)) { + continue; + } + graph_add_edge_with_weight(g_prim, u, v, w); + set_add(visited, v); + update_heap(g, pq, visited, v); + } + pqueue_free(pq); + set_free(visited); + + return g_prim; +} diff --git a/src/graph/tarjan.c b/src/graph/tarjan.c index df53c0c3..a87e2679 100644 --- a/src/graph/tarjan.c +++ b/src/graph/tarjan.c @@ -12,7 +12,7 @@ struct TarjanContext { }; void free_tarjan_context(struct TarjanContext *tc) { - // keep on memor: g_tarjan + componentes + // keep on memory: g_tarjan + components free(tc->exploration); free(tc->complete); stack_free(tc->path); diff --git a/src/graph/test.c b/src/graph/test.c index fe862fcc..679f8a4e 100644 --- a/src/graph/test.c +++ b/src/graph/test.c @@ -204,7 +204,7 @@ void test_graph_edges_ordered() { 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"); + printf(":: graph\n"); graph_print(g); @@ -389,9 +389,11 @@ void test_graph_dijkstra(bool extra_tests) { graph_free(dijkstra_result); } -void test_graph_kruskal() { +void test_graph_kruskal(bool extra_tests) { + char cwd[PATH_MAX]; + getcwd(cwd, sizeof(cwd)); puts("== Graph kruskal test"); - Graph *g = graph_create(); + Graph *g = graph_undirected_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); @@ -402,14 +404,57 @@ void test_graph_kruskal() { printf(":: input graph\n"); graph_print(g); - printf(":: kruskal tree\n"); + printf(":: kruskal minimum spanning tree\n"); Graph *g_kruskal = graph_kruskal(g); graph_print(g_kruskal); + if (extra_tests) { + graph_export_to_dot(g, "test_graph_kruskal_input.dot"); + printf("saved input: %s/test_graph_kruskal_input.dot\n", cwd); + graph_export_to_dot(g_kruskal, "test_graph_kruskal_output.dot"); + printf("saved output: %s/test_graph_kruskal_output.dot\n", cwd); + } + + int s = graph_edges_sum(g_kruskal); + printf("Cost sum: %d\n", s); + assert(s == 31); graph_free(g); graph_free(g_kruskal); } +void test_graph_prim(bool extra_tests) { + char cwd[PATH_MAX]; + getcwd(cwd, sizeof(cwd)); + puts("== Graph prim test"); + Graph *g = graph_undirected_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(":: prim minimum spanning tree\n"); + Graph *g_prim = graph_prim(g, 5); + graph_print(g_prim); + + if (extra_tests) { + graph_export_to_dot(g, "test_graph_prim_input.dot"); + printf("saved input: %s/test_graph_prim_input.dot\n", cwd); + graph_export_to_dot(g_prim, "test_graph_prim_output.dot"); + printf("saved output: %s/test_graph_prim_output.dot\n", cwd); + } + + int s = graph_edges_sum(g_prim); + printf("Cost sum: %d\n", s); + assert(s == 31); + graph_free(g); + graph_free(g_prim); +} + bool should_run_extra_tests(int argc, char *argv[]) { // Iterate through the command-line arguments starting from argv[1] @@ -434,7 +479,8 @@ int main(int argc, char *argv[]) { test_graph_topological_sort(); test_graph_dijkstra(extra_tests); test_graph_edges_ordered(); - test_graph_kruskal(); + test_graph_kruskal(extra_tests); + test_graph_prim(extra_tests); 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 6fc8d484..12821d9e 100644 --- a/src/list/single/list.c +++ b/src/list/single/list.c @@ -255,6 +255,19 @@ List* list_remove(List *l, int data) { return l; } +List* list_remove_by_key_data(List *l, int key, int data) { + if (!list_empty(l)) { + if (l->key == key && l->data == data) { + List* next = l->next; + free(l); + l = next; + } else { + l->next = list_remove_by_key_data(l->next, key, data); + } + } + return l; +} + List* list_remove_by_key(List *l, int key){ if (!list_empty(l)) { if (l->key == key) { diff --git a/src/list/single/list.h b/src/list/single/list.h index 40ca2308..fac18421 100644 --- a/src/list/single/list.h +++ b/src/list/single/list.h @@ -170,6 +170,17 @@ List* list_remove(List *l, int data); */ List* list_remove_by_key(List *l, int key); + +/** + * @brief Remove specific element from List by a key equal data + * @param l List to remove with \p key + * @param key integer value to remove + * @param data integer value to remove + * @return new list without the node which contains \p key and \p data + * @ingroup DataStructureMethods + */ +List* list_remove_by_key_data(List *l, int key, int data); + /** * @brief Free memory of List and its nodes * @ingroup DataStructureMethods diff --git a/src/pqueue/pqueue.c b/src/pqueue/pqueue.c index f8dff4e9..f7d0ad3f 100644 --- a/src/pqueue/pqueue.c +++ b/src/pqueue/pqueue.c @@ -87,11 +87,11 @@ void min_heapify(PQueue *pq, int i) { PQueue* pqueue_create(PQueueType type) { PQueue* pq = (PQueue*) malloc(sizeof(PQueue)); - pq->index = hash_table_create(PQUEUE_SIZE * 10); if (pq == NULL) { perror("Failed to allocate memory for PQueue"); exit(EXIT_FAILURE); } + pq->index = hash_table_create(PQUEUE_SIZE * 10); pq->heap = (PQueueNode*) malloc(PQUEUE_SIZE * sizeof(PQueueNode)); if (pq->heap == NULL) { perror("Failed to allocate memory for PQueue heap"); diff --git a/src/utils/pair_hash.h b/src/utils/pair_hash.h new file mode 100644 index 00000000..ba2c0773 --- /dev/null +++ b/src/utils/pair_hash.h @@ -0,0 +1,31 @@ +#include + +/* @brief Pair function of Szudzik (2006). + * @details Useful to encapsulate two integers as a unique reversible integer hash. + * @return hashed integer. + * @see https://en.wikipedia.org/wiki/Pairing_function#Cantor_pairing_function + */ +static inline int pair_hash(int x, int y) { + if (x < y) { + return y * y + x; + } + return x * x + x + y; +} + +static inline int unpair_hash_x(int z) { + int z_sqrt = floor(sqrt(z)); + int z_rest = z - z_sqrt * z_sqrt; + if (z_rest < z_sqrt) { + return z_rest; + } + return z_rest; +} + +static inline int unpair_hash_y(int z) { + int z_sqrt = floor(sqrt(z)); + int z_rest = z - z_sqrt * z_sqrt; + if (z_rest < z_sqrt) { + return z_sqrt; + } + return z_rest - z_sqrt; +}