Summaries

University notes in Markdown format from Obsidian.

Un grafo è costituito da una coppia (N, A) dove N è l'insieme dei nodi e A è l'insieme di coppie di nodi.

  • Grafo orientato è un grafo in cui gli archi sono coppie di nodi ordinati:

    grafoOrientato
    grafoOrientato

  • Grafo non orientato è un grafo in cui gli archi non sono rappresentati da coppie ordinate:

    grafoNonOrientato
    grafoNonOrientato

Il numero di archi possibili in un grafo sono:

  • MAX:
  • MIN: n(n-1)/2

che equivale come complessità a circa O(n²).

  • Grafo denso: nel caso di grafi con n² archi si parla di grafo denso
  • Grafo sparso: nel caso di grafi in cui ci sono circa n archi (circa 1 per ogni nodo) si parla di grafo sparso

Adiacenza (orientati)

Dato un arco (v, w) si dice che w è adiacente a v e che l'arco esce da v ed entra in w.

Grado (orientati)

Si possono distinguere due tipi di gradi:

  • grado di entrata: numero archi entranti da un nodo
  • grado di uscita: numero archi uscenti da un nodo

Cammino

Un cammino da v a w è una sequenza di archi distinti (v1, v2) (v2, v3) ... (vK-1, vK) tale che v = v1 e w = vK ed ha lunghezza K-1.

Ciclo

È un cammino attraverso cui, a partire da un nodo x, è possibile ritornare su di esso. Un grafo che contiene un ciclo viene definito ciclico sennò aciclico.

Grafo non ordinato connesso

Se esiste un cammino che collega ogni coppia di nodi, una componente connessa è un insieme massimale di nodi tale che esiste un cammino che collega ogni coppia di nodi di essa.

Grafo ordinato debolmente connesso e fortemente connesso

Si definisce fortemente connesso se per ogni coppia di nodi esiste un cammino orientato. Si definisce debolmente connesso se il grafo non orientato ottenuto da esso è connesso.

Grafi pesati

Una differenza importante tra grafo e albero è che in quest'ultimo è possibile definire un nodo di entrata. Inoltre il contenuto informativo dei grafi non è limitato solamente ai nodi ma anche gli archi.

Rappresentazione dei grafi

Il problema principale nella rappresentazione dei grafi è che ogni nodo deve essere direttamente accessibile quindi è necessario avere una qualche struttura dati che lo permetta.

Inoltre è possibile creare un'altra struttura dati che conservi il contenuto informativo del nodo indipendente dalla strategia di rappresentazione degli archi.

Le più comuni modalità di rappresentazione di quest'ultimi:

  • Matrice di adiacenza: più velocità nella lettura dell'esistenza di un arco ma spreco di memoria (se non è denso)
  • Liste di adiacenza: più velocità nella lettura dei nodi adiacenti ma meno velocità nella lettura dell'esistenza di un arco

Classi

class Grafo {
    protected:
        n nodi, m archi...
        matrice/lista adiacenti
    public:
        Grafo(unsigned n)
        bool operator() (unsigned i, unsigned j) // verifica se l'arco (i, j) esiste
        void operator() (unsigned i, unsigned j, bool b) // se voglio aggiungere l'arco (i, j)
        int n() // numero nodi
}

Esempio di utilizzo

Scrivere una funzione che, ricevuto un grafo non orientato, restituisca, per ciascun nodo, il suo grado.

Vector<int> numAdiacenti(const Grafo& G) {
    Vector<int> grado(G.n());
    for(int i = 0; i < G.n(); i++) {
        grado[i] = 0;
        for(int j = 0; j < G.n(); j++) {
            if(i != j && G(i, j)) // O(1) oppure O(n)
                grado[i]++;
        }
    }
    return grado;
}

La funzione ha O(n²) in caso di matrice di adiacenza sennò O(n³). Lo stesso algoritmo possiamo risolverlo in funzione dell'arco:

Arco e;
grado[i] = 0;
for(e = G.primoAd(i); !e.nullo; e = G.succAd(e)) grado[i]++;

Con questo algoritmo possiamo avere una complessità costante di O(n²).