In un file di tipo testo è descritto, in modo ridondante, un grafo non

Transcript

In un file di tipo testo è descritto, in modo ridondante, un grafo non
GRAFO
In un file di tipo testo è descritto, in modo
ridondante, un grafo non orientato come
quello mostrato nell’esempio in figura, secondo il seguente formato:
nodo-1 nodo_adiacente-11 ... nodo_adiacente-1j
nodo-2 nodo_adiacente-21 ... nodo_adiacente-2k
...................
nodo-m nodo_adiacente-m ... nodo_adiacente-mn
A
B
C
E
D
1
I
nodi
sono
identificati
dalle
lettere
dell’alfabeto.
Per il grafo in esempio, il file conterrebbe le
seguenti linee:
ABDE
BACD
CBD
DABCE
EAD
2
Realizzare il diagramma di flusso di un programma che, dopo aver letto il file di descrizione di un grafo, richieda il nome di un
nodo d’inizio e di un nodo di fine e produca
in output tutti i possibili percorsi dal primo
al secondo nodo, senza ripetizioni e non ripassando due volte per lo stesso nodo.
3
Per il grafo in esempio, tutti i percorsi dal
nodo B al nodo E sono i seguenti:
BDE-BDAE-BCDAE-BCDEBADE-BAE
Soluzione:
La base dati per mantenere la descrizione
del grafo sarà un vettore di record, detto
adiacenze. In ciascun record c’è:
un campo, num_adiac, che contiene il
numero di nodi adiacenti al primo,
4
un campo vettore di caratteri, vett_adiac,
che memorizza ciascun nodo esistente e
il nome dei nodi adiacenti.
La dimensione di questo vettore di record
può essere limitato a 26, quante sono le lettere dell’alfabeto.
Per cercare una soluzione del problema
proposto, faccio riferimento all’esempio riportato nel testo.
5
Immagino di costruire un percorso inserendo in successione gli identificatori dei nodi
nel campo vett_nodi di un record di supporto che chiamo percorso.
Un campo num_nodi dello stesso record
conterrà il numero di nodi attualmente presente.
Il primo nodo è necessariamente quello fornito in input come nodo di partenza.
6
Per scegliere il secondo, devo tener conto di
tutti i nodi connessi al primo.
Per ciascuno dei nodi in seconda posizione,
poi, devo considerare tutti i nodi ad essi
connessi, e così via.
Mi accorgo che per costruire i percorsi non
è sufficiente il record di supporto, poiché
devo memorizzare tutte le scelte per la seconda posizione e, per ciascuna di queste,
tutte le scelte per la terza e così avanti.
7
Ricorro quindi ad una struttura di supporto
più complessa, lo stack, in cui memorizzare
i percorsi parziali man mano che vengono
creati.
Per uno stack sono definite le funzioni:
push(record): memorizza record in cima
allo stack;
pop(record): estrae l’elemento in cima allo
stack e lo pone in record;
8
stack_vuoto: fornisce il valore VERO se lo
stack è vuoto.
Procedo così. All’inizio lo stack è vuoto.
Pongo nel campo vett_nodi del record percorso, in prima posizione, il nodo di partenza, B, e metto 1 nel campo num_nodi.
Inserisco il record nello stack, che si presenta così
1B
9
Ora entro in un ciclo in cui:
1) estraggo un elemento dallo stack ponendolo in percorso;
1B
2) cerco la posizione dell’ultimo elemento
del campo vett_nodi (B nel nostro caso)
nella matrice adiacenze;
3) per ognuno dei nodi adiacenti
3.1) lo aggiungo nel campo vett_nodi, aggiustando il campo num_nodi
10
3.2) inserisco il record nello stack
Dopo queste operazioni, lo stack si presenta
così (immaginando che lo stack cresca verso il basso):
2 BA
2 BC
2 BD
Se immagino di eseguire di nuovo il passo
1), in percorso avrò:
2 BD
11
e, dopo aver eseguito tutti i passi successivi,
nello stack avrò:
2
2
3
3
3
3
BA
BC
B DA
B DB
B DC
B DE
Sono così in grado di costruire tutti i percorsi possibili. Il procedimento è da raffinare.
12
Dopo aver aggiunto un nodo adiacente nel
campo vett_nodi di percorso, devo verificare che questo non sia il nodo finale che voglio raggiungere.
Se accade questo caso, non inserirò il record
nello stack, ma piuttosto segnalerò il successo e visualizzerò il percorso.
Se non si tratta di un nodo finale, dovrò verificare che il nodo che ho appena inserito
non sia già presente:
13
in questo caso si tratterebbe di un percorso
chiuso (ciclo) che devo escludere, evitando
di inserire percorso nello stack.
Il procedimento prosegue finché lo stack
non si svuota.
14
Base dati:
percorso: record composto dai seguenti
campi:
num_nodi: intero;
vett_nodi: vettore di caratteri lungo 26;
new_percorso: come percorso;
15
adiacenze: vettore di record composti dai
seguenti campi:
num_adiac: intero;
vett_adiac: vettore di caratteri lungo 26.
16
Algoritmo:
1 Leggo_adiacenze
2 Inizializzo_stack
3 Leggo da tastiera nodo_iniziale e nodo_finale
4 Pongo
nodo_iniziale
nel
campo
vett_nodi di percorso, in prima posizione
5 Pongo 1 nel campo num_nodi di percorso
17
6 Inserisco
percorso
nello
stack
/* uso push(percorso) */
7 Finché stack_vuoto dà FALSO
7.1estraggo un elemento dallo stack e lo
pongo in percorso
pop(percorso) */
18
/* uso
7.2cerco in adiacenze il nodo contenuto
nel campo vett_nodi nella posizione
data da num_nodi; utilizzo la funzione
cerca_nodo,
che
restituisce
l’indice nel vettore, posiz_nodo
7.3con indice_adiac che va da 1 al valore contenuto nel campo num_adiac
dell’elemento di adiacenze indicato
da posiz_nodo
19
7.3.1pongo in nodo_adiacente il
nome del nodo che si trova in
adiacenze[posiz_nodo], nel campo vett_adiac[indice_adiac]
7.3.2copio percorso in new_percorso
7.3.3memorizzo nodo_adiacente in
new_percorso,
incrementando
num_nodi
7.3.4se
nodo_adiacente
do_finale
20
=
no-
7.3.4.1stampo “Trovato percorso”
7.3.4.2stampo_nodi
7.3.5altrimenti
21
7.3.5.1se cerca_circuiti fornisce
FALSO
7.3.5.1.1memorizzo
new_percorso nello stack
/* uso push(new_percorso)
*/
8 Fine.
22
Funzione leggo_adiacenze
1 Apro il file file_in in lettura
2 Pongo posiz_adiac a 0
3 Pongo indice_adiac a 0
4 Finché (carat = getc(file_in)) è
da
EOF
4.1se carat = new_line
4.1.1pongo indice_adiac nel campo
num_adiac
di
ze[posiz_adiac]
23
adiacen-
4.1.2pongo indice_adiac a 0
4.1.3incremento posiz_adiac
4.2altrimenti
4.2.1pongo carat in nel campo
vett_adiac[indice_adiac]
di
adiacenze[posiz_adiac]
4.2.2incremento indice_adiac
5 Chiudo file_in
6 Memorizzo posiz_adiac in totale_nodi
7 Fine.
24
Nell’ipotesi che la descrizione del grafo sia
coerente e completa, la variabile totale_nodi è ridontante e serve solo per debug.
Funzione
cerca_nodo(nodo_da_cercare,
posiz_nodo)
1 Pongo posiz_nodo a 0
2 Pongo trovato a FALSO
25
3 Finché trovato è FALSO
3.1se il confronto tra nodo_da_cercare
e l’elemento vett_adiac[0] di adiacenze[posiz_nodo] dà VERO
3.1.1pongo trovato a VERO
3.2altrimenti
3.2.1incremento posiz_nodo
4 Fine.
26
Funzione stampo_nodi
1 Con indice che va da 0 al valore del
campo num_nodi di percorso
1.1stampo il campo vett_nodi[indice] di
percorso
1.2se indice < num_nodi - 2
/* esclu-
do il trattino dopo l’ultimo nodo */
1.2.1stampo ‘-’
2 Fine.
27
Funzione cerca_circuiti(new_percorso)
1 Pongo trovato a FALSO
2 Pongo in
num_elementi
il
campo
num_nodi di new_percorso
3 Pongo indice a 0
4 Finché (trovato è FALSO) e insieme
(indice < (num_elementi - 1))
4.1se
l’elemento
vettore[indice]
di
new_percorso è uguale all’elemento
vettore[num_elementi - 1]
28
4.1.1pongo trovato a VERO
4.2altrimenti
4.2.1incremento indice
5 Restituisco trovato
6 Fine.
29
Mi posso occupare ora della realizzazione
dello stack. Ho due possibilità: una lista
concatenata (allocazione dinamica della
memoria) oppure un vettore (allocazione
statica).
La prima soluzione è generalmente da preferire perché pone vincoli meno stringenti
alla dimensione massima che può assumere
la struttura.
30
La seconda invece, richiedendo la definizione statica di un vettore, pone limiti più pesanti ed inoltre blocca l’uso di una grossa
porzione di memoria.
Tuttavia scelgo questa seconda soluzione,
poiché è di facile realizzazione e permette
un accesso più veloce (non richiede di allocare dinamicamente porzioni di memoria,
ma semplicemente di modificare un indice!).
31
Lo stack sarà costituito da un vettore di record, detto vett_stack, e da un indice, indice_stack, il quale punta sempre al primo
elemeto libero (punta cioè oltre la “cima”
dello stack).
Pertanto deve essere inizializzato con la
prima posizione del vettore, quella con indice 0.
La base dati che costituisce lo stack deve essere definita come globale.
32
Base dati:
vett_stack: vettore di record composti dai
seguenti campi:
num_nodi: intero;
vett_nodi: vettore di caratteri lungo 26;
indice_stack: intero;
Algoritmo:
Funzione inizializzo_stack
1 Pongo indice_stack a 0
2 Fine.
33
Funzione push(record)
1 Copio
record
vett_stack[indice_stack]
2 Incremento indice_stack
3 Fine.
34
in
Funzione pop(record)
1 Decremento indice_stack
2 Copio vett_stack[indice_stack] in record
3 Fine.
35
Funzione stack_vuoto
1.Pongo
in
flag_vuoto
il
valore
dell’espressione relazionale indice_stack =
0
2.Restituisco flag_vuoto
3.Fine.
36
Note:
la funzione pop non è protetta: deve essere eseguita solo dopo il test che lo stack
non sia vuoto!
La funzione push non controlla che ci sia
memoria disponibile per l'inserimento!
Per gestire correttamente queste (e altre
eventuali) situazioni d'errore occorrerebbe
complicare notevolmente l'algoritmo.
37