Graphdurchlauf

Werbung
ACM ICPC
Praktikum
Kapitel 9: Graphdurchlauf
Übersicht
•
•
•
•
•
•
Arten von Graphen
Datenstrukturen für Graphen
Breitensuche
Tiefensuche
Zusammenhangskomponenten
Topologische Sortierung
Arten von Graphen
• Ein Graph G=(V,E) besteht aus einer
Kontenmenge V und Kantenmenge E.
• G ungerichtet: E µ { {u,v} | u,v 2 V}
• Beispiel eines ungerichteten Graphen:
Knoten
Kante
Arten von Graphen
• G gerichtet: E µ V £ V
• Beispiel eines gerichteten Graphen:
• G gewichtet: Gewichtsfunktion für Knoten
oder Kanten
Arten von Graphen
• G is azyklisch, wenn es keinen
(gerichteten) Kreis hat, und sonst zyklisch
• Beispiel eines gerichteten azyklischen
Graphen (in der Literatur oft als DAG bez.)
Arten von Graphen
• G heißt einfach, wenn keine Kante doppelt
vorkommt
• Beispiel eines nicht-einfachen Graphen:
• G ist beschriftet, falls die Knoten/Kanten
Namen haben, sonst unbeschriftet
Datenstrukturen für Graphen
• G=(V,E) enthalte n Knoten und m Kanten
• Adjazenzmatrix: Verwende n £ n-Matrix
A[i,j] 2 {0,1} und setze A[i,j] auf 1 genau
dann, wenn (i,j) bzw. {i,j} in E.
Geeignet für dichte Graphen, d.h. m groß
• Array von Adjazenzlisten: Vewende Array
V der Größe n. V[i] speichert die Liste der
benachbarten Knoten für Knoten i.
Geeignet für dünne Graphen, d.h. m klein
Datenstrukturen für Graphen
• Array von Kanten: Verwende Array der
Größe m, um alle Kanten abzuspeichern.
Geeignet für sehr dünne Graphen.
• Reine Pointerstruktur: Jeder Knoten ist ein
Objekt mit Pointern, die Kanten
repräsentieren, zu anderen Knoten.
Geeignet für dynamische Graphen.
Beispiel für Datenstruktur
1.
2.
#define MAXV
#define MAXDEGREE
100
50
/* maximum number of vertices */
/* maximum outdegree of a vertex */
3.
4.
5.
6.
7.
8.
typedef struct {
int edges[MAXV+1][MAXDEGREE];
/* adjacency info */
int degree[MAXV+1];
/* outdegree of each vertex */
int nvertices;
/* number of vertices in the graph */
int nedges;
/* number of edges in the graph */
} graph;
9. initialize_graph(graph *g)
10. {
11.
int i;
12.
13.
g -> nvertices = 0;
g -> nedges = 0;
14.
15. }
for (i=1; i<=MAXV; i++) g->degree[i] = 0;
/* counter */
Beispiel für Datenstruktur
1. read_graph(graph *g, bool directed)
2. {
3.
int i;
/* counter */
4.
int m;
/* number of edges */
5.
int x, y;
/* vertices in edge (x,y) */
6.
initialize_graph(g);
7.
scanf("%d %d",&(g->nvertices),&m);
8.
9.
10.
11.
12. }
for (i=1; i<=m; i++) {
scanf("%d %d",&x,&y);
insert_edge(g,x,y,directed);
}
Beispiel für Datenstruktur
1. insert_edge(graph *g, int x, int y, bool directed)
2. {
3.
if (g->degree[x] > MAXDEGREE)
4.
printf("Warning: insertion(%d,%d) exceeds max
degree\n",x,y);
5.
6.
g->edges[x][g->degree[x]] = y;
g->degree[x] ++;
7.
8.
9.
10.
11. }
if (directed == FALSE)
insert_edge(g,y,x,TRUE);
else
g->nedges ++;
Beispiel für Datenstruktur
1. delete_edge(graph *g, int x, int y, bool directed)
2. {
3.
int i;
/* counter */
4.
5.
6.
7.
for (i=0; i<g->degree[x]; i++)
if (g->edges[x][i] == y) {
g->degree[x] --;
g->edges[x][i] = g->edges[x][g->degree[x]];
8.
9.
if (directed == FALSE)
delete_edge(g,y,x,TRUE);
10.
11.
return;
12.
13. }
}
printf("Warning: deletion(%d,%d) not found in g.\n",x,y);
Beispiel für Datenstruktur
1. print_graph(graph *g)
2. {
3.
int i,j;
/* counters */
4.
5.
6.
7.
8.
9.
10. }
for (i=1; i<=g->nvertices; i++) {
printf("%d: ",i);
for (j=0; j<g->degree[i]; j++)
printf(" %d",g->edges[i][j]);
printf("\n");
}
Breitensuche
• Problem: durchsuche Graph G=(V,E)
systematisch nach Eigenschaften
• Lösung: Breitensuche (breadth-firstsearch, BFS) oder Tiefensuche (depthfirst-search, DFS). Jeder besuchte Knoten
bzw. jede besuchte Kante wird markiert,
um doppeltes Besuchen zu vermeiden.
Breitensuche
1. bool processed[MAXV]; /* which vertices have been processed */
2. bool discovered[MAXV]; /* which vertices have been found */
3. int parent[MAXV];
/* discovery relation */
4. bool finished = FALSE; /* if true, cut off search immediately */
5. initialize_search(graph *g)
6. {
7.
int i;
/* counter */
8.
9.
10.
11.
12. }
for (i=1; i<=g->nvertices; i++) {
processed[i] = discovered[i] = FALSE;
parent[i] = -1;
}
Breitensuche
1.
2.
3.
4.
5.
bfs(graph *g, int start)
{
queue q;
int v;
int i;
/* queue of vertices to visit */
/* current vertex */
/* counter */
6.
7.
8.
init_queue(&q);
enqueue(&q,start);
discovered[start] = TRUE;
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24. }
while (empty(&q) == FALSE) {
v = dequeue(&q);
process_vertex(v);
processed[v] = TRUE;
for (i=0; i<g->degree[v]; i++)
if (valid_edge(g->edges[v][i]) == TRUE) {
if (discovered[g->edges[v][i]] == FALSE) {
enqueue(&q,g->edges[v][i]);
discovered[g->edges[v][i]] = TRUE;
parent[g->edges[v][i]] = v;
}
if (processed[g->edges[v][i]] == FALSE)
process_edge(v,g->edges[v][i]);
}
}
Breitensuche - Demo
1.
2.
process_vertex(int v)
{ printf("processed vertex %d\n",v); }
3.
4.
process_edge(int x, int y)
{ printf("processed edge (%d,%d)\n",x,y); }
5.
6.
bool valid_edge(int e)
{ return (TRUE); }
7. main()
8. {
9.
graph g;
10.
int i;
11.
12.
13.
14.
15.
16.
17.
18.
19. }
read_graph(&g,FALSE);
print_graph(&g);
initialize_search(&g);
bfs(&g,1);
for (i=1; i<=g.nvertices; i++) printf(" %d",parent[i]);
printf("\n");
for (i=1; i<=g.nvertices; i++) find_path(1,i,parent);
printf("\n");
Anwendung: Suche nach Pfad
•
Problem: finde Pfad von „start” nach “end”.
1. find_path(int start, int end, int parents[])
2. {
3.
if ((start == end) || (end == -1))
4.
printf("\n%d",start);
5.
else {
6.
find_path(start,parents[end],parents);
7.
printf(" %d",end);
8.
}
9. }
Tiefensuche
1.
2.
3.
4.
dfs(graph *g, int v)
{
int i;
int y;
/* counter */
/* successor vertex */
5.
if (finished) return;
/* allow for search termination */
6.
7.
discovered[v] = TRUE;
process_vertex(v);
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
for (i=0; i<g->degree[v]; i++) {
y = g->edges[v][i];
if (valid_edge(g->edges[v][i]) == TRUE) {
if (discovered[y] == FALSE) {
parent[y] = v;
dfs(g,y);
} else
if (processed[y] == FALSE)
process_edge(v,y);
}
if (finished) return;
}
20.
21. }
processed[v] = TRUE;
Tiefensuche - Demo
1.
2.
process_vertex(int v)
{ printf("processed vertex %d\n",v); }
3.
4.
5.
6.
7.
process_edge(int x, int y)
{
if (parent[x] == y) printf("processed tree edge (%d,%d)\n",x,y);
else printf("processed back edge (%d,%d)\n",x,y);
}
8.
9.
bool valid_edge(int e)
{ return (TRUE); }
10.
11.
main()
{
graph g;
12.
int i;
13.
14.
15.
16.
read_graph(&g,FALSE);
print_graph(&g);
initialize_search(&g);
dfs(&g,1);
17.
18.
19.
for (i=1; i<=g.nvertices; i++) find_path(1,i,parent);
printf("\n");
}
Anwendung 1: suche Kreis
•
Problem: suche nach Kreis in ungerichtetem Graph
1. process_edge(int x, int y)
2. {
3.
if (parent[x] != y) { /* found back edge! */
4.
printf(“Cycle from %d to %d:”, y, x);
5.
find_path(y,x,parent);
6.
finished = TRUE;
7. } }
8. process_vertex(int v) { }
Anwendung 2:
Zusammenhangskomponenten
•
Problem: finde Zusammenhangskomponenten in ungerichtetem Graph
1.
2.
3.
4.
connected_components(graph *g)
{
int c; /* component number */
int i; /* counter */
5.
6.
7.
8.
9.
10.
11.
12.
13. }
initialize_search(g);
c = 0;
for (i=1; i<=g->nvertices; i++)
if (discovered[i] == FALSE) {
c = c+1;
printf(“Component %d: “, c);
dfs(g,i);
printf(“\n”);
}
14. process_vertex(int v) { print(“ %d”, v); }
15. process_edge(int x, int y) { }
Anwendung 3:
Topologisches Sortieren
•
Problem: sortiere Knoten in Reihe s, so dass für jede Kante (i,j) gilt:
s(i) < s(j)
•
Lösung:
1. compute_indegrees(graph *g, int in[])
2. {
3.
int i,j;
/* counters */
4.
for (i=1; i<=g->nvertices; i++) in[i] = 0;
5.
6.
7. }
for (i=1; i<=g->nvertices; i++)
for (j=0; j<g->degree[i]; j++) in[ g->edges[i][j] ] ++;
Anwendung 3:
Topologisches Sortieren
1.
2.
3.
4.
5.
6.
topsort(graph *g, int sorted[])
{
int indegree[MAXV];
queue zeroin;
int x, y;
int i, j;
/* indegree of each vertex */
/* vertices of indegree 0 */
/* current and next vertex */
/* counters */
7.
8.
9.
10.
compute_indegrees(g,indegree);
init_queue(&zeroin);
for (i=1; i<=g->nvertices; i++)
if (indegree[i] == 0) enqueue(&zeroin,i);
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
j=0;
while (empty(&zeroin) == FALSE) {
j = j+1;
x = dequeue(&zeroin);
sorted[j] = x;
for (i=0; i<g->degree[x]; i++) {
y = g->edges[x][i];
indegree[y] --;
if (indegree[y] == 0) enqueue(&zeroin,y);
}
}
22.
23.
24.
if (j != g->nvertices)
printf("Not a DAG -- only %d vertices found\n",j);
}
Anwendung 3:
Topologisches Sortieren
1. main()
2. {
3.
graph g;
4.
int out[MAXV];
5.
int i;
6.
7.
read_graph(&g,TRUE);
print_graph(&g);
8.
topsort(&g,out);
9.
10.
11.
for (i=1; i<=g.nvertices; i++)
printf(" %d",out[i]);
printf("\n");
12. }
Herunterladen