Вы находитесь на странице: 1из 11

ALGORITMO Dijkstra: implementao utilizando matriz de adjacncias Para esta primeira implementao assumimos um grafo G(veja figura abaixo)

representado por uma matriz de adjacncias.

Representao Grfica de G

Estrutura de dados para Matriz de Adjacncias do Grafo G


#define #define #define #define INFINITY 32768 MEMBER 1 NONMEMBER 0 MAXNODES 50

typedef struct graph GRAPH; struct arc { // se adj=1 ento ADJACENTE; se adj=0 ento NO ADJACENTE int adj; // peso da aresta int weight; }; struct graph { // matriz de adjacncias struct arc arcs[MAXNODES][MAXNODES]; };

Matriz de Adjacncias de G

Cada vrtice do grafo representado por um valor inteiro entre 0 a MAXNODES - 1. Um array bi-dimensional do tipo arc (encapsulado na struct graph) define a matriz de adjacncias do grafo. O campo adj na struct arc, informa (armazena) a existncia de uma relao de adjacncia (adj = 1), ou no (adj = 0) entre dois vrtices da matriz. O campo weight armazena o peso

da aresta que estes mesmos dois vrtices, quando adj = 1, ou um valor bastante grande ("infinito") quando no h esta relao (adj = 0).

Procedimentos inicializao do grafo


// inicializa matriz de adjacncia que representa o grafo // retorna ponteiro para esta matriz (tipo GRAPH) GRAPH* init_graph () { GRAPH *gTemp = (GRAPH *) malloc(sizeof(GRAPH)); for (int i = 0; i < MAXNODES; i++) { for (int j = 0; j < MAXNODES; j ++) { gTemp->arcs[i][j].adj = 0; gTemp->arcs[i][j].weight = INFINITY; } } return gTemp; }

A funo init_graph deve ser chamada pelo usurio do tipo abstrato de dados GRAPH para a inicializao da matriz de adjacncias do grafo. Alm de alocar memria, a funo inicializa todos os campos adj e weight do grafo como 0 e "infinito" respectivamente.

Procedimentos para manuteno do grafo (matriz de adjacncia)


// cria uma aresta que "liga" (incide) em dois ns e atribui o respectivo peso; // recebe o grafo, dois ns (node1 e node2) e o peso (wt) da aresta void joinwt (GRAPH *g, int node1, int node2, int wt) { g->arcs[node1][node2].adj = 1; g->arcs[node1][node2].weight = wt; } // remove uma aresta do grafo; // recebe o grafo e os dois ns (node1 e node2); void remove (GRAPH *g, int node1, int node2) { g->arcs[node1][node2].adj = 0; g->arcs[node1][node2].weight = INFINITY; }

Os procedimentos acima so utilizados para manuteno do grafo, ou seja, criao e atribuio de pesos s arestas ou remoo de arestas.

Algoritmo de Dijkstra
void dijkstra (GRAPH *g, int s, int t) { int dist[MAXNODES], perm[MAXNODES], path[MAXNODES]; int current, i, k, dc; int smalldist, newdist; /* Inicializa INFINITY */ for (i = 0; i perm[i] = dist[i] = } todos os ndices de 'perm' como 0 e de 'dist' como < MAXNODES; i++) { NONMEMBER; INFINITY;

/* Inclui 's' em perm (perm[s]=1) e configura(armazena) sua distancia como sendo zero */ perm[s] = MEMBER; dist[s] = 0; /* define 's' como origem (fonte) da busca */ current = s; k = current; while (current != t) { smalldist = INFINITY; dc = dist[current]; for (i = 0; i < MAXNODES; i++) { //se o elemento NO est em perm if (perm[i] == NONMEMBER) { //calcula distncia a partir do vrtice corrente ao vrtice adjacente i newdist = dc + g->arcs[current][i].weight; //se a distncia partindo do vrtice corrente for menor, atualiza o vetor //de distncias e de precedncia if (newdist < dist[i]) { dist[i] = newdist; path[i] = current; } //determina o vrtice (entre todos os no pertencentes a perm) com menor distncia if (dist[i] < smalldist) { smalldist = dist[i]; k = i; } } } /* fim for */ /* embora estamos assumindo grafos ponderados e conexos, este

if garante que em caso de no existncia de um caminho o programa no entre em loop infinito */ if (current == k) { printf("\n\nCAMINHO NAO EXISTE\n\n"); return; } current = k; perm[current] = MEMBER; } /* fim while */ /* impressao do resultado ****************/ printf("\n\nRESULTADO: "); int caminho = t; printf("%d <- ", t); while (caminho != s) { printf("%d", path[caminho]); caminho = path[caminho]; if (caminho != s) printf (" <- "); } printf("\n\ncusto: %d\n\n", dist[t]); /****************************************/ } /* fim dijkstra */

O algoritmo aqui demonstrado recebe como parmetro um grafo (tipo GRAPH), um vrtice de origem s e o um vrtice de destino t. Note que esta implementao no ir necessariamente calcular o custo mnimo a partir de s para todo vrtice do grafo, porque uma vez encontrado o caminho de custo mnimo para t (vrtice de interesse) o algoritmo finalizado. Uma implementao que calcularia a distncia mnima para todos os vrtices poder ser implementada facilmente com pouqussimas alteraes sobre o algoritmo aqui dado. O procedimento dijkstra utiliza-se de 3 arrays, dist[i], perm[i] e path[i], que armazenam para todo vrtice i do grafo suas respectivas distncias, situao com relao ao conjunto PERM e o vrtice precedente a i no caminho de custo mnimo. Veja que este ltimo array necessrio para imprimir o caminho completo do vrtice s a t. Exemplo: uma situao onde tenhamos distance[i] = 5, perm[i] = 1 e path=[i] = 2, temos que o vrtice i j teve seu caminho mnimo encontrado (perm[i]=1), que a distncia se s a i 5 e que o vrtice que o precede neste caminho o vrtice de ndice 2.

A varivel current armazena o ndice do vrtice includo mais recentemente em PERM (perm[current]=1). A medida que um vrtice se torna current este j teve sua distncia mnima calculada. Ento, todos os outros vrtices adjacentes a ele devem ter suas distncias recalculadas e atualizadas se necessrio.

Anlise de complexidade de tempo do procedimento Dijkstra Veja que o lao while ser executado no pior caso n vezes (n o nmero de vrtices do grafo). Alm disso, cada iterao neste lao envolve uma pesquisa em todos os ns do grafo (for (i = 0; i < MAXNODES; i++) para atualizao de distncias e descoberta do elemento com menor distncia no pertencente a PERM. No dificil perceber que este algoritmo tem complexidade O(n2).

Algoritmo de Dijkstra

O algoritmo de Dijkstra o mais famoso dos algoritmos para clculo de caminho de custo mnimo entre vrtices de um grafo e, na prtica, o mais empregado. Escolhido um vrtice como raiz da busca, este algoritmo calcula o custo mnimo deste vrtice para todos os demais vrtices do grafo. O algoritmo pode ser usado sobre grafos orientados (dgrafos), ou no, e admite que todas as arestas possuem pesos no negativos (nulo possvel). Esta restrio perfeitamente possvel no contexto de redes de transportes, onde as arestas representam normalmente distncias ou tempos mdios de percurso; podero existir, no entanto, aplicaes onde as arestas apresentam pesos negativos, nestes casos o algoritmo no funcionar corretamente.

Funcionamento do algoritmo Assumiremos um conjunto, chama-lo-emos PERM, que contm inicialmente apenas o vrtice fonte (raiz da busca) s. A qualquer momento PERM contm todos os vrtices para os quais j foram determinados os menores caminhos usando apenas vrtices em PERMa partir de s. Para cada vrtice z fora de PERM matemos a menor distncia dist[z] de s a z usando caminhos onde o nico vrtice que no est em PERM seja z. necesssrio tambm armazenar o vrtice adjacente (precedente) a z neste caminho em path[z]. Como fazer com que PERM cresa, ou seja, qual vrtice deve ser includo em PERM a seguir ? Tomamos o vrtice, entre todos os que ainda no pertencem a PERM, com menor distncia dist. Acrescentamos ento este vrtice, chamemo-lo de current, a PERM, e recalculamos as distncias (dist) para todos os vrtices adjacentes a ele que no estejam em PERM, pois pode haver um caminho menor a partir de s, passando por current, do que aquele que havia antes de current ser agregado a PERM. Se houver um caminho mais curto precisamos tambm atualizar path[z] de forma a indicar que current o vrtice adjacente a z pelo novo caminho mnimo.

Vejamos o funcionamento do algoritmo sob uma outra representao: 1) Defini-se inicialmente o n de origem (raiz), neste caso s, e inclui-se este n em PERM. Atribui-se zero a sua distncia (dist[s]) porque o custo de ir

de s a s obviamente 0. Todos os outros ns i tem suas distncias (dist[i]) inicializadas com um valor bastante grande ("infinito").

2) A partir de s consulta-se os vrtices adjacentes a ele, que no grafo G so u e x. Para todos os vrtices adjacentes, que chamaremos z, calcula-se: Se dist[z] > dist[s] + peso(s, z) dist[z] = dist[s] + peso(s, z) path[z] = s Fim Se

3) Dentre todos os vrtices no pertencentes a PERM escolhe-se aquele com a menor distncia. Neste caso o vrtice x, pois dist[x] = 5.

4) Ento, inclui-se x em PERM e a partir de x consulta-se os vrtices adjacentes a ele que no esto em PERM, que no grafo G so u, v e y. Para todos os vrtices adjacentes, que chamaremos z, calcula-se: Se dist[z] > dist[x] + peso(x, z) dist[z] = dist[x] + peso(x, z) path[z] = x Fim Se

5) Dentre todos os vrtices no pertencentes a PERM escolhe-se aquele com a menor distncia. Neste caso o vrtice y, pois dist[y] = 7.

6) Inclui-se ento y em PERM e a partir de y consulta-se os vrtices adjacentes a ele que no esto em PERM, que no grafo G apenas o vrtice v. Se dist[v] > dist[y] + peso(y, v) dist[v] = dist[y] + peso(y, v) path[v] = y Fim Se

7) Dentre todos os vrtices no pertencentes a PERM escolhe-se aquele com a menor distncia. Neste caso o vrtice u, pois dist[u] = 8.

8) Inclui-se ento u em PERM e a partir de u consulta-se os vrtices adjacentes a ele que no esto em PERM, que no grafo G apenas o vrtice v. Se dist[v] > dist[u] + peso(u, v) dist[v] = dist[u] + peso(u, v) path[v] = u Fim Se

9) Dentre todos os vrtices no pertencentes a PERM escolhe-se aquele com a menor distncia. Neste caso o nico vrtice restante v e dist[v] = 9.

10

10) Por fim faz-se v pertencer a PERM. Neste ponto, todos os vrtices j esto em PERM e a busca finalizada.

Subsections Analisaremos agora, nos links abaixo, implementaes do algoritmo de Dijkstra em grafos representados por matriz e lista de adjacncias.

Matriz de Adjacncias Lista de Adjacncias

11