HappyHippyHippo Posted September 29, 2016 at 03:20 AM Report #599220 Posted September 29, 2016 at 03:20 AM Após ler o artigo na revisto Programar, edição 53 de Agosto de 2016, com o tema Algoritmo de Dijkstra, só me ocorreu uma coisa. A autora do artigo foi a própria a afirmar que a sua implementação é somente uma de várias maneiras de abordar o problema. No entanto, eu nunca gostei a solução de uso de uma tabela/matriz para a resolução do problema. Sempre achei que é um desperdício de memória e origina um código menos intuitivo do que o algoritmo realmente está a fazer. Para isso, aqui está uma solução em C, que usa linked lists e uma priority queue (que para mim é provavelmente a solução mais fácil de perceber) main.c #include "graph.h" #include <stdlib.h> #include <stdio.h> #define SOURCE "A" #define DESTINATION "E" int main(int argc, char ** argv) { Graph * graph = NULL; if ((graph = graph_create()) == NULL) { printf("Error allocation graph memory\n"); exit(-1); } graph_add_node(graph, "G"); graph_add_node(graph, "F"); graph_add_node(graph, "E"); graph_add_node(graph, "D"); graph_add_node(graph, "C"); graph_add_node(graph, "B"); graph_add_node(graph, "A"); graph_node_add_edge(graph_get_node(graph, "A"), 14, graph_get_node(graph, "F")); graph_node_add_edge(graph_get_node(graph, "A"), 9, graph_get_node(graph, "C")); graph_node_add_edge(graph_get_node(graph, "A"), 7, graph_get_node(graph, "B")); graph_node_add_edge(graph_get_node(graph, "B"), 15, graph_get_node(graph, "D")); graph_node_add_edge(graph_get_node(graph, "B"), 10, graph_get_node(graph, "C")); graph_node_add_edge(graph_get_node(graph, "B"), 7, graph_get_node(graph, "A")); graph_node_add_edge(graph_get_node(graph, "C"), 2, graph_get_node(graph, "F")); graph_node_add_edge(graph_get_node(graph, "C"), 11, graph_get_node(graph, "D")); graph_node_add_edge(graph_get_node(graph, "C"), 10, graph_get_node(graph, "B")); graph_node_add_edge(graph_get_node(graph, "C"), 9, graph_get_node(graph, "A")); graph_node_add_edge(graph_get_node(graph, "D"), 6, graph_get_node(graph, "E")); graph_node_add_edge(graph_get_node(graph, "D"), 11, graph_get_node(graph, "C")); graph_node_add_edge(graph_get_node(graph, "D"), 15, graph_get_node(graph, "B")); graph_node_add_edge(graph_get_node(graph, "E"), 9, graph_get_node(graph, "F")); graph_node_add_edge(graph_get_node(graph, "E"), 6, graph_get_node(graph, "D")); graph_node_add_edge(graph_get_node(graph, "F"), 9, graph_get_node(graph, "E")); graph_node_add_edge(graph_get_node(graph, "F"), 2, graph_get_node(graph, "C")); graph_node_add_edge(graph_get_node(graph, "F"), 14, graph_get_node(graph, "A")); graph_print(graph); GraphEdge ** path = graph_path(graph, graph_get_node(graph, SOURCE), graph_get_node(graph, DESTINATION)); if (!(* path)) { printf("No path"); } else { printf("N(%s) ", SOURCE); GraphEdge ** it = path; while (* it) { printf("-> E(%2d -> %s)", (*it)->cost, (*it)->target->ID); it++; } } free(path); graph_destroy(& graph); return 0; } graph.h #ifndef GRAPH_H #define GRAPH_H /// Tipos de dados usados na definição de uma (di)grafo typedef struct GraphEdge GraphEdge; typedef struct GraphNode GraphNode; typedef struct Graph Graph; /// Estrutura usada para guardar a informação de uma aresta do (di)grafo struct GraphEdge { GraphNode * owner; GraphEdge * next; unsigned int cost; GraphNode * target; }; /// Estrutura usada para guardar a informação de um nó do (di)grafo struct GraphNode { Graph * owner; GraphNode * next; char * ID; GraphEdge * edge_list; unsigned int edge_count; struct { GraphEdge * path; unsigned int cost; unsigned int steps; } tree; }; /// Estrutura usada para guardar a informação de um (di)grafo struct Graph { GraphNode * node_list; unsigned int node_count; }; /// Função construtora de uma (di)grafo /// /// @return Ponteiro para o (di)grafo criado Graph * graph_create(void); /// Função de destruição/libertação de memória de um (di)grafo /// /// @param graph Referência para o ponteiro pque guarda o (di)grafo a ser /// destruido /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_destroy(Graph ** graph); /// Função usada para adicionar um novo nó ao (di)grafo /// /// @param graph Referência ao (di)grafo que irá conter o nó criado /// @param ID String com o nome identificador do nó a ser criado /// /// @return Ponteiro para o nó criado GraphNode * graph_add_node(Graph * graph, char * ID); /// Função usada para obter um nó do (di)grafo /// /// @param graph Referência ao (di)grafo que contem o nó a ser obtido /// @param ID String com o nome identificador do nó a ser obtido /// /// @return Ponteiro para o nó encontrado GraphNode * graph_get_node(Graph * graph, char * ID); /// Função usada para remover um nó do (di)grafo /// /// @param graph Referência ao (di)grafo que contem o nó a ser removido /// @param ID String com o nome identificador do nó a ser removido /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_remove_node(Graph * graph, char * ID); /// Função usada para adicionar uma nova aresta a um nó do (di)grafo /// /// @param node Referência ao nó do (di)grafo que irá conter a nova aresta /// @param cost Valor de custo de transitar na aresta /// @para target Referência ao nó destino da aresta /// /// @return Ponteiro para a aresta criado GraphEdge * graph_node_add_edge(GraphNode * node, unsigned int cost, GraphNode * target); /// Função usada para obter uma aresta de um nó do (di)grafo /// /// @param node Referência ao nó do (di)grafo que contem a aresta a ser obtida /// @param index Índice da aresta a ser obtida /// /// @return Ponteiro para a aresta obtida GraphEdge * graph_node_get_edge(GraphNode * node, unsigned int index); /// Função usada para remover uma aresta de um nó do (di)grafo /// /// @param node Referência ao nó do (di)grafo que contem a aresta a ser /// removida /// @param index Índice da aresta a ser removida /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_node_remove_edge(GraphNode * node, unsigned int index); /// Função de escrita do (di)grafo na consola /// /// @param graph Referência do (di)grafo a ser imprimido /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_print(Graph * graph); /// Função de determinação do caminho mais curto /// /// @param graph Referência do (di)grafo onde a pesquisa de percurso irá ser /// efectuada /// @param src Referência ao nó de origem do caminho a ser pesquisado /// @param dest Referência ao nó de destino do caminho a ser pesquisado /// /// @return Lista de aresta que compoem o caminho encontrado /// Esta lista é alocada internamente e deverá ser libertada /// posteriormente GraphEdge ** graph_path(Graph * graph, GraphNode * src, GraphNode * dest); #endif graph.c #include "graph.h" #include <stdlib.h> #include <stdio.h> #include <string.h> // PRIVATE ---------------------------------------------------------------- /// Função de destruição de uma aresta /// /// @param edge Referência ao ponteiro que guarda a aresta a ser destruida /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_destroy_edge(GraphEdge ** edge) { // validar argumentos if (!edge || !(*edge)) return -1; // libertar a memória da aresta free(* edge); * edge = NULL; return 0; } /// Função de destruição de um nó /// /// @param node Referência ao ponteiro que guarda o nóa a ser destruido /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_destroy_node(GraphNode ** node) { // validar argumentos if (!node || !(*node)) return -1; // ciclo de libertação de memória das arestas do nó GraphNode * aux = (GraphNode *) * node; while (aux->edge_count) { GraphEdge * del = aux->edge_list; aux->edge_list = aux->edge_list->next; aux->edge_count--; graph_destroy_edge(& del); } // libertar a memória do nó free((* node)->ID); free(* node); * node = NULL; return 0; } /// Função de impressão de uma aresta na consola /// /// @param edge Referência â aresta que irá ser imprimida /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_print_edge(GraphEdge * edge) { // validar argumentos if (!edge) return -1; // aprestar a informação da aresta printf(" E(%2d -> %s),", edge->cost, edge->target->ID); return 0; } /// Função de impressão de um nó na consola /// /// @param node Referência ao nó que irá ser imprimido /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_print_node(GraphNode * node) { // validar argumentos if (!node) return -1; // aprestar a informação do nó printf(" N(%s) -> ", node->ID); // ciclo de apresentação das arestas do nó GraphEdge * edge = node->edge_list; while (edge) { graph_print_edge(edge); edge = edge->next; } printf("\n"); return 0; } /// Função usada para remover todas as arestas de um nó que tem como destino um /// nó definido pelo ID dado como argumento da chamada da função /// Esta implementação é necessária para garantir a coerencia dos dados após a /// remoção de um nó de forma a eliminiar aresta para nós removidos /// /// @param node Referência do nó que contem as arestas a serem elimindas /// @param ID Identificador do nó de destino das arestas a serem eliminadas /// /// @return Valor inteiro que indica sucesso da operação ao ter o valor de zero /// ou erro caso contrário int graph_node_remove_edges_to(GraphNode * node, char * ID) { // validar argumentos if (!node || !ID) return -1; GraphEdge * del = NULL; // ciclo de remoção das arestas que sáo encontradas no início da lista while (node->edge_list && strcmp(node->edge_list->target->ID, ID) == 0) { del = node->edge_list; node->edge_list = node->edge_list->next; graph_destroy_edge(& del); node->edge_count--; } // ciclo de pesquisa e remoção das arestas no mei oda lista GraphEdge * it = node->edge_list; while (it && it->next) { if (strcmp(it->next->target->ID, ID) == 0) { del = it->next; it->next = it->next->next; graph_destroy_edge(& del); node->edge_count--; } else it = it->next; } return 0; } /// Tipo de dados que implementa um priority queue de arestas a ser usada /// durante a execução do "dijkstra algorihtm" typedef struct GraphEdgeQueue { struct GraphEdgeQueue * next; GraphEdge * edge; } GraphEdgeQueue; /// Função que insere uma aresta na queue tendo como parâmetro de prioridade, o /// custo associado ao processo dado pelo custo a aresta adicionado ao custo /// já calculado para chegar ao nó origem da aresta inserida /// /// @param queue Referência à queue que irá guardar a aresta a ser inserida /// @param edge Referência à aresta a ser inserida na queue /// /// @return Referência à queue final GraphEdgeQueue * graph_edge_queue_push(GraphEdgeQueue * queue, GraphEdge * edge) { // validar argumentos if (!edge) return queue; // alocar memória para a nova entrada da queue GraphEdgeQueue * entry = NULL; if ((entry = malloc(sizeof(GraphEdgeQueue))) == NULL) return NULL; // inicializar a entrada entry->edge = edge; entry->next = NULL; // verificar se a queue se encnotra vazia ou que mesmo assim, a aresta // deverá ser inserida na primeira posição da queue (maior prioridade) if (!queue || queue->edge->owner->tree.cost + queue->edge->cost > entry->edge->owner->tree.cost + entry->edge->cost) { entry->next = queue; queue = entry; } else { // ciclo de pesquisa pela posição da queue onde a aresta deverá ser // inserida GraphEdgeQueue * it = queue; while (it->next && it->next->edge->owner->tree.cost + it->next->edge->cost <= entry->edge->owner->tree.cost + entry->edge->cost) it = it->next; // inserir a entrada na posição encontrada entry->next = it->next; it->next = entry; } return queue; } /// Função usada para remover um elemento da queue /// /// @param queue Referência à queue que contem a aresta a ser removida /// @param edge Referência para um ponteiro que irá guardar a aresta removida /// /// @return Referência à queue final GraphEdgeQueue * graph_edge_queue_pop(GraphEdgeQueue * queue, GraphEdge ** edge) { // validar argumentos if (!queue || !edge) return queue; // remoção da entrada à cabeça GraphEdgeQueue * del = queue; queue = queue->next; * edge = del->edge; // libertação da memoria alocada para a entrada da queue free(del); return queue; } // PUBLIC ---------------------------------------------------------------- Graph * graph_create(void) { Graph * graph = NULL; // alocação de memória para o (di)grafo if ((graph = malloc(sizeof(Graph))) != NULL) { graph->node_list = NULL; graph->node_count = 0; } return graph; } int graph_destroy(Graph ** graph) { // validar argumentos if (!graph || !(*graph)) return -1; // ciclo de libertação de memória dos nós do (di)grafo Graph * aux = (Graph *) * graph; while (aux->node_count) { GraphNode * del = aux->node_list; aux->node_list = aux->node_list->next; aux->node_count--; graph_destroy_node(& del); } // libertação de memória free(* graph); * graph = NULL; return 0; } GraphNode * graph_add_node(Graph * graph, char * ID) { // validar argumentos if (!graph || !ID) return NULL; GraphNode * node = NULL; // alocação de memória para o nó a ser inserido if ((node = malloc(sizeof(GraphNode))) == NULL) return NULL; size_t length = strlen(ID) + 1; if ((node->ID = malloc(length)) == NULL) { free(node); return NULL; } // inicialização do nó criado strcpy(node->ID, ID); node->owner = graph; node->next = graph->node_list; node->edge_list = NULL; node->edge_count = 0; node->tree.path = NULL; node->tree.cost = 0; node->tree.steps = 0; // inserir o nó à cabeça da lista de nós do (di)grafo graph->node_list = node; graph->node_count++; return node; } GraphNode * graph_get_node(Graph * graph, char * ID) { // validar argumentos if (!graph || !ID) return NULL; // ciclo de pesquisa pelo no pretendido GraphNode * node = graph->node_list; while (node && strcmp(node->ID, ID) != 0) node = node->next; return node; } int graph_remove_node(Graph * graph, char * ID) { // validar argumentos if (!graph || !ID) return -1; // verificar se o (di)grafo náo tem nós if (!graph->node_list) return -2; GraphNode * it = NULL; GraphNode * del = NULL; // verificar se o nó a ser removido encontrasse à cabeça da lista if (strcmp(graph->node_list->ID, ID) == 0) { del = graph->node_list; graph->node_list = graph->node_list->next; } else { // ciclo de pesquisa pelo nó a ser removido it = graph->node_list; while (it->next && strcmp(it->next->ID, ID) != 0) it = it->next; // verificar se o nó foi encontrado e remover-lo da lista if (it->next) { del = it->next; it->next = it->next->next; } } // verificar se o nó foi encontrado if (!del) return -2; // remover de todos os nós que ficaram na lista, as arestas que possuem como // destino, o nó removido it = graph->node_list; while (it) { graph_node_remove_edges_to(it, ID); it = it->next; } // destruição do nó removido graph_destroy_node(& del); graph->node_count--; return 0; } GraphEdge * graph_node_add_edge(GraphNode * node, unsigned int cost, GraphNode * target) { // validar argumentos if (!node || !target || node->owner != target->owner) return NULL; // alocação de memória da aresta a ser criada GraphEdge * edge = NULL; if ((edge = malloc(sizeof(GraphEdge))) == NULL) return NULL; // inicialização da aresta alocada edge->owner = node; edge->next = node->edge_list; edge->cost = cost; edge->target = target; // inserir a aresta criada à cabeça de aresta do nó node->edge_list = edge; node->edge_count++; return edge; } GraphEdge * graph_node_get_edge(GraphNode * node, unsigned int index) { // validar argumentos if (!node || node->edge_count <= index) return NULL; // ciclo de pesquisa da aresta pretendida unsigned int it = 0; GraphEdge * edge = node->edge_list; while (it != index) { edge = edge->next; it++; } return edge; } int graph_node_remove_edge(GraphNode * node, unsigned int index) { // validar argumentos if (!node || node->edge_count <= index) return -1; GraphEdge * del = NULL; // verificar se a aresta a ser removido se necontra à cabeça da lista if (index == 0) { del = node->edge_list; node->edge_list = node->edge_list->next; } else { // ciclo de pesquisa pela aresta a ser removida unsigned int it = 0; GraphEdge * edge = node->edge_list; while (it != index - 1) { edge = edge->next; it++; } // remove a aresta encontrada da lista del = edge->next; edge->next = edge->next->next; } // destruição da aresta removida graph_destroy_edge(& del); node->edge_count--; return 0; } int graph_print(Graph * graph) { // validar argumentos if (!graph) return -1; printf("Graph :\n"); // ciclo de impressãodos nós do (di)grafo GraphNode * node = graph->node_list; while (node) { graph_print_node(node); node = node->next; } return 0; } GraphEdge ** graph_path(Graph * graph, GraphNode * src, GraphNode * dest) { // validar argumentos if (!graph || !src || src->owner != graph || !dest || dest->owner != graph) return NULL; // inicialização dos dados de criação/composição da árvore de menor custo GraphNode * node = graph->node_list; while (node) { node->tree.path = NULL; node->tree.cost = 0; node->tree.steps = 0; node = node->next; } GraphEdgeQueue * queue = NULL; GraphNode * current = src; GraphEdge * edge = NULL; // ciclo que implementa o "dijkstra algorithm" current->tree.path = (GraphEdge * ) 1; do { // ciclo de inserção da lista de aresta do último nó que foi inserido na // árvore de menor custo edge = current->edge_list; while (edge) { // verificar se a aresta tem como destino um nó que já se encontra // na árvore de menor custo if (!edge->target->tree.path) queue = graph_edge_queue_push(queue, edge); edge = edge->next; } // verificar se exsitem arestas na queue if (queue) { // pesquisa pela aresta de menor custo na queue que não como destino // um nó que já se encontra na árvore de menor custo do { edge = NULL; queue = graph_edge_queue_pop(queue, & edge); } while (edge && edge->target->tree.path); // verificar se void encontrada um aresta válida if (edge) { // inserir o nó destino da aresta na árvore de menor custo current = edge->target; current->tree.path = edge; current->tree.cost = edge->owner->tree.cost + edge->cost; current->tree.steps = edge->owner->tree.steps + 1; } } } while (queue && current != dest); // ciclo de limpeza/destruição da queue while (queue) queue = graph_edge_queue_pop(queue, &edge); // gerar a lista de arestas que indicará a caminho encontrado GraphEdge ** path = NULL; unsigned int path_length = graph->node_count + 1; if ((path = malloc(sizeof(GraphEdge *) * path_length)) == NULL) return NULL; memset(path, 0, sizeof(GraphEdge *) * path_length); // verificar se o algoritmo encontrou um caminho if (current == dest) { // ciclo de escrita/composição do caminho encontrado no array alocado while (current != src) { path[current->tree.steps - 1] = current->tree.path; current = current->tree.path->owner; } } return path; } 2 Report IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now