Salta al contenuto
Note per Studenti Rappresentazione dei grafi

Rappresentazione dei grafi

In questa pagina 5

Sia G=(V,E)G = (V, E) con nn vertici ed mm archi (i vertici si indicano spesso con gli interi 1..n1..n o 0..n−10..n-1). Tutte le rappresentazioni partono da due strutture di base:

Ogni vertice o arco può avere campi aggiuntivi (ID, peso, etichette, parent) richiesti dall'algoritmo. Le liste sono quelle di Liste, pile e codeRipasso degli ADT elementari: lista index-based (array) e position-based (lista doppiamente concatenata con sentinelle), pila LIFO, coda FIFO, deque, iteratori; interfacce, implementazioni e costi; array estendibile e coda circolare.Liste, pile e code → (Liste concatenate in CLista singolarmente concatenata in C con nodo struct e testa passata per riferimento (Nodo **); addHead, addTail ricorsiva e iterativa, pop, inversione in loco, liberazione; coda con puntatori a testa e coda; ricorsione sulle liste; costi.Liste concatenate in C → per l'implementazione) e i riferimenti tra nodi sono puntatori (Puntatori in CUn puntatore contiene un indirizzo di memoria; operatori & e *, NULL, puntatori come parametri per modificare variabili del chiamante, aritmetica dei puntatori, legame tra array e puntatori, const, puntatori a puntatori.Puntatori in C →).

Liste di adiacenza

Definizione (liste di adiacenza). Per ogni vertice vv una lista I(v)I(v) di puntatori agli archi di LEL_E incidenti su vv (in alternativa, ai vicini; al posto delle liste si possono usare mappe, ad esempio Tabelle hashTabella hash per implementare una mappa: funzione hash = hash code + compression function, bucket array, separate chaining; hash code per numeri e stringhe (polynomial, cyclic shift), division e MAD; load factor, complessità al caso pessimo Theta(n) e medio O(1+lambda), rehashing; esempio svolto con inserimenti e collisioni.Tabelle hash →: con una mappa areAdjacent costerebbe Θ(1)\Theta(1) in media). È la rappresentazione più usata, e quella assunta se non detto altrimenti, perché:

  • occupa spazio lineare nella taglia del grafo, Θ(n+m)\Theta(n + m) (in un grafo non diretto ogni arco compare nelle liste dei suoi due estremi);
  • permette di scorrere i vicini di vv in tempo lineare nel grado di vv.

Esempio. Con archi (1,2),(1,3),(2,3),(3,4)(1,2),(1,3),(2,3),(3,4) e il vertice 55 isolato: I(1)={2,3}I(1)=\{2,3\}, I(3)={1,2,4}I(3)=\{1,2,4\} e I(5)=∅I(5)=\emptyset.

Matrice di adiacenza

Definizione (matrice di adiacenza). Matrice AA di dimensione n×nn \times n, con righe e colonne in corrispondenza 1-1 con i vertici (che devono essere rappresentati da interi):

A[i1,i2]={puntatore a e=(i1,i2)∈LEse l’arco esistenullaltrimenti.A[i_1, i_2] = \begin{cases} \text{puntatore a } e = (i_1, i_2) \in L_E & \text{se l'arco esiste} \\ \text{null} & \text{altrimenti.} \end{cases}

Dà accesso a un arco in tempo costante, ma occupa Θ(n2)\Theta(n^2), che può essere superlineare nella taglia del grafo: conviene per grafi densi (m=Θ(n2)m = \Theta(n^2)).

Esempio. Con n=5n=5 la matrice ha 2525 celle; A[1,3]A[1,3] punta all'arco (1,3)(1,3) se esiste, altrimenti vale null.

Confronto

Proprietà (spazio e tempi delle rappresentazioni di un grafo)

Operazione / risorsa Solo LEL_E Liste di adiacenza Matrice di adiacenza
spazio Θ(n+m)\Theta(n + m) Θ(n+m)\Theta(n + m) Θ(n2)\Theta(n^2)
incidentEdges(v) Θ(m)\Theta(m) Θ(degree(v))\Theta(\text{degree}(v)) Θ(n)\Theta(n)
opposite(v, e) Θ(1)\Theta(1) Θ(1)\Theta(1) Θ(1)\Theta(1)
areAdjacent(u, v) Θ(m)\Theta(m) Θ(min⁡(degree(u),degree(v)))\Theta(\min(\text{degree}(u), \text{degree}(v))) Θ(1)\Theta(1)
scansione di tutti i vicini di tutti i vertici Θ(nm)\Theta(nm) Θ(n+m)\Theta(n + m) Θ(n2)\Theta(n^2)

Esempio. Con n=1000n=1000 vertici e m=3000m=3000 archi le liste di adiacenza usano Θ(n+m)=4000\Theta(n+m)=4000 celle (più precisamente n+2m=7000n+2m=7000 riferimenti, perché ogni arco è in due liste), la matrice n2=106n^2=10^6.

Come si legge la tabella. incidentEdges(v) con sola LEL_E costringe a guardare tutti gli mm archi per trovare quelli incidenti su vv; con le liste basta percorrere I(v)I(v), lunga degree(v)\text{degree}(v); con la matrice si legge tutta la riga vv (nn celle, quasi tutte vuote in un grafo sparso). areAdjacent(u,v) con le liste si fa scorrendo in parallelo I(u)I(u) e I(v)I(v) e fermandosi appena una finisce, quindi in Θ(min⁡(degree(u),degree(v)))\Theta(\min(\text{degree}(u),\text{degree}(v))); con la matrice si legge la cella A[u,v]A[u,v], un solo accesso diretto (Θ(1)\Theta(1) come l'indicizzazione di un array nel modello RAM, Problemi computazionali e algoritmiProblema computazionale come insieme di coppie (istanza, soluzione); algoritmo e modello di calcolo RAM; pseudocodice; taglia di un'istanza; ADT e struttura dati concreta; esempio svolto con ricerca lineare e binaria in un array ordinato.Problemi computazionali e algoritmi →). Il grafico confronta lo spazio per un grafo sparso con m=3nm=3n (ogni vertice ha in media grado 66):

Grafico interattivo: Memoria per un grafo con m = 3n archi: liste di adiacenza n + 2m = 7n riferimenti, matrice di adiacenza n² celle; a n = 100 sono 700 contro 10000, e il rapporto cresce con n

Rappresentare gli archi solo con LEL_E rende lenti gli algoritmi che esplorano i vicini; per questo le visite (vedi Visite di grafi - BFS e DFSVisite in ampiezza (BFS) e in profondità (DFS) come design pattern; etichette discovery, cross e back edge; BFS tree e distanze; complessità Theta(n+m) con liste di adiacenza; applicazioni (connettività, componenti, spanning tree, cammini minimi non pesati, cicli, vertici a distanza al più d); esempio svolto su un grafo di 7 vertici.Visite di grafi - BFS e DFS →) usano le liste di adiacenza, e le loro complessità Θ(n+m)\Theta(n + m) dipendono da questa scelta (con la matrice sarebbero Θ(n2)\Theta(n^2)).

Esempio

Grafo con vertici 1..51..5 e archi e1=(1,2)e_1 = (1,2), e2=(1,3)e_2 = (1,3), e3=(2,3)e_3 = (2,3), e4=(3,4)e_4 = (3,4) (il vertice 55 è isolato).

1 2 3 4 5
1 0 1 1 0 0
2 1 0 1 0 0
3 1 1 0 1 0
4 0 0 1 0 0
5 0 0 0 0 0

Numero di vertici isolati (richiesto in un esame): si scorrono i vertici e si contano quelli con lista di adiacenza vuota: Θ(n)\Theta(n) con le liste (non serve guardare gli archi); con la matrice occorrerebbe Θ(n2)\Theta(n^2).

Errori comuni

  • Usare la matrice di adiacenza per un grafo sparso: spreca spazio (Θ(n2)\Theta(n^2) contro Θ(n+m)\Theta(n + m)) e rende le visite quadratiche.
  • Dimenticare che in un grafo non diretto ogni arco compare due volte nelle liste di adiacenza (e due volte nella matrice, simmetrica).
  • Esprimere il costo dello scorrimento dei vicini come Θ(n)\Theta(n) invece di Θ(degree(v))\Theta(\text{degree}(v)).

Versione ripasso

Esercizi su questo argomento

Teoria collegata