Corso IN110 Algoritmi e Strutture Dati

Gli esercizi

Testi e soluzioni di alcuni esercizi

Algoritmo di Dijkstra per la costruzione dei cammini minimi con sorgente singola

/*
**  dijkstra.c
**
**  Letto in input un grafo orientato e connesso, rappresentato
**  con una matrice di adiacenza e tale che ad ogni spigolo
**  (u,v) sia associato un peso non negativo w(u,v), per ogni
**  vertice v calcola il minimo cammino per raggiungere v
**  partendo dalla sorgente s (letta in input). Il programma
**  e' una codifica in C dell'algoritmo di Dijkstra. La coda H
**  viene rappresentata come una coda di priorita' realizzata
**  con un heap binario.
**
**  Pseudo-codifica dell'algoritmo:
**
**  Dijkstra(G, W, s)
**    per ogni v in V(G) ripeti:
**      d(v) = infinito
**      padre(v) = nil
**    end
**    d(s) = 0
**    aggiungi ogni vertice v alla coda di priorita' H
**    fintanto che H non e` vuoto ripeti:
**      sia u il l'elemento di H con d(u) minima
**      estrai u da H
**      per ogni vertice v adiacente a u ripeti:
**        se d(v) > d(u) + w(u,v) allora
**          d(v) = d(u) + w(u,v)
**          padre(v) = u
**          riposiziona v in H
**        end
**      end
**    end
**  END
**
*/


#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

#define MAX 50

/*
 *  Elemento della lista di adiacenza del grafo
 */

struct nodo {
  int info;
  int w;
  struct nodo *next;
};

/*
 *  Scambia il contenuto dei due indirizzi di memoria x e y
 *  ricevuti come argomento.
 */

void scambia(int *x, int *y) {
  int z;
  z = *x;
  *x = *y;
  *y = z;
  return;
}

/*
 *  Legge in input la lista dei vertici adiacenti ad
 *  un vertice del grafo. Oltre a memorizzare il vertice
 *  adiacente in p->info, memorizza il peso dello spigolo 
 *  in p->w.
 */

struct nodo *leggiLista(void) {
  int i, n;
  struct nodo *p, *primo;
  printf("Numero di elementi: ");
  scanf("%d", &n);
  primo = NULL;
  for (i=0; i<n; i++) {
    p = malloc(sizeof(struct nodo));
    printf("Vertice e peso: ");
    scanf("%d %d", &p->info, &p->w);
    p->next = primo;
    primo = p;
  }
  return(primo);
}

/*
 *  Legge in input il grafo e lo rappresenta attraverso
 *  n liste di adiacenza.
 */

int leggiGrafo(struct nodo *G[]) {
  int i, n;
  printf("Numero di vertici: ");
  scanf("%d", &n);
  for (i=0; i<n; i++) {
    printf("Lista di adiacenza del vertice %d.\n", i);
    G[i] = leggiLista();
  }
  return(n);
}

/*
 *  Visualizza in output una lista di adiacenza del grafo
 */

void stampaLista(struct nodo *p) {
  while (p != NULL) {
    printf("%2d (%d) --> ", p->info, p->w);
    p = p->next;
  }
  printf("NULL\n");
  return;
}

/*
 *  Visualizza in output tutte le liste di adiacenza che
 *  compongono il grafo.
 */

void stampaGrafo(struct nodo *G[], int n) {
  int v;
  for (v=0; v<n; v++) {
    printf("%3d: ", v);
    stampaLista(G[v]);
  }
  return;
}

/*
 *  Estrae l'elemento u dalla coda di priorita' H rappresentata
 *  come un heap binario, con l'elemento minimo posto sulla radice
 *  (min-heap)
 */

int heapExtract(int H[], int Hind[], int d[]) {
  int i, min;
  min  = H[1];
  H[1] = H[H[0]];
  Hind[H[1]] = 1;
  H[0] = H[0] -1 ;
  i = 1;
  while (i < H[0]/2 && (d[H[i]] > d[H[2*i]] || d[H[i]] > d[H[2*i+1]])) {
    if (d[H[2*i]] > d[H[2*i+1]]) {
      scambia(&H[i], &H[2*i]);
      Hind[H[i]] = i;
      Hind[H[2*i]] = 2*i;
      i = 2*i;
    } else {
      scambia(&H[i], &H[2*i+1]);
      Hind[H[i]] = i;
      Hind[H[2*i+1]] = 2*i+1;
      i = 2*i+1;
    }
  }
  if (i == H[0]/2 && d[H[i]] > d[H[2*i]]) {
    scambia(&H[i], &H[2*i]);
    Hind[H[i]] = i;
    Hind[H[2*i]] = 2*i;
  }
  return(min);
}

/*
 *  Ricolloca il vertice v nell'Heap dopo che ne รจ stato diminuito
 *  il valore d[v].
 */

void heapRelocate(int H[], int Hind[], int v, int d[]) {
  int i;
  i = Hind[v];
  while (i > 1 && d[H[i]] < d[H[i/2]]) {
    scambia(&H[i], &H[i/2]);
    Hind[H[i]] = i;
    Hind[H[i/2]] = i/2;
    i = i/2;
  }
  return;
}

/*
 *  Inizializza l'heap H inserendo s sulla radice e gli altri
 *  vertici a seguire. 
 *  Hind[v] e' l'indice di v nell'array H
 */

void heapInit(int H[], int Hind[], int n, int s) {
  int v, k=2;
  H[0] = n;
  H[1] = s;
  Hind[s] = 1;
  for (v=0; v<n; v++) {
    if (v != s) {
      H[k] = v;
      Hind[v] = k;
      k++;
    }
  }
  return;
}
      
/*
 *  Algoritmo di Dijkstra per il calcolo del cammino
 *  di costo minimo dalla sorgente s
 */

void dijkstra(struct nodo *G[], int n, int d[], int pi[], int s) {
  int u, v, H[MAX], Hind[MAX], infinito = INT_MAX;
  struct nodo *p;
  for (v=0; v<n; v++) {
    d[v] = infinito;
    pi[v] = -1;
  }
  d[s] = 0;
  heapInit(H, Hind, n, s);
  while (H[0] != 0) {
    u = heapExtract(H, Hind, d);
    p = G[u];
    while (p != NULL) {
      v = p->info;
      if (d[v] > d[u] + p->w) {
        pi[v] = u;
        d[v] = d[u] + p->w;
        heapRelocate(H, Hind, v, d);
      }
      p = p->next;
    }
  }
  return;
}

/*
 *  Funzione principale che implementa l'algoritmo di Dijkstra.
 */

int main(void) {
  int d[MAX], pi[MAX], n, s, v;
  struct nodo *G[MAX];
  n = leggiGrafo(G);
  stampaGrafo(G, n);
  printf("Sorgente: ");
  scanf("%d", &s);
  dijkstra(G, n, d, pi, s);
  for (v=0; v < n; v++) {
    printf("d[%d] = %3d, pi[%d] = %3d\n", v, d[v], v, pi[v]);
  }
  return(0);
}

Università degli Studi Roma Tre - Dipartimento di Matematica e Fisica - Corso di laurea in Matematica - Corso di Informatica 1 (IN110)

Author: Marco Liverani - Last modified: Tuesday December 10, 2024 - URI: http://193.204.165.209/users/liverani/IN110/dijkstra.shtml