aufg8_loes - oth

Werbung
Fachhochschule Regensburg
Aufgabenblatt 8: Topologisches Sortieren
Algorithmen und Datenstrukturen
Name: ________________________
Aufgabensteller: Prof. Sauer
Vorname: _____________________
Januar 2002
Topologisches Sortieren:
Sortieren bedeutet Herstellung einer totalen (vollständigen) Ordnung. Es gibt auch Prozesse zur
Herstellung von teilweisen Ordnungen, d.h.: Es gibt eine Ordnung für einige Paare dieser Elemente,
aber nicht für alle.
1. Aufgabe
a) Gegeben ist die binäre Relation R = {(1,2),(1,3),(2,4),(2,5),(2,6),(3,5),(3,7),(5,7),(6,7)}.
Gib an, wie sich diese binäre Relation durch einen gerichteten Graphen (anschaulich) darstellen lässt.
Die Relation beschreibt über die paarweisen Angaben Knotenidentifikationen. Ein Paar besteht aus
einem Startknoten und ein Zielknoten, im Graphen wird dies durch einen Pfeil vom Startknoten zum
Zielknoten beschrieben.
Pfeile im Graphen bestimmen die Ordnungsrelation (topologische Sortierung). Die dadurch bestimmte
Ordnung kann in eine lineare Ordnung eingebettet werden.
b) Zeige diese lineare Ordnung (topologische Sortierung) durch Anordnung der Knoten in einer Reihe,
so dass die Pfeile nach rechts zeigen.
d) Wie kann man die unter b) angegebene Folge einfach auf der Konsole (über ein Rechenprogramm)
darstellen?
1
Für azyklische Graphen kann man die Knoten-Identifikationen als Folge beschreiben. Bezogen auf
eine Kante (Pfeil) mit Anfangsknoten-Identifikation I und Endknoten-Identifikation J, erscheint die
Knoten-Identifikation I vor Knotenidentifikation J.
2. Aufgabe
Gegeben ist der folgende azyklische, gerichtete Graph
2
1
4
3
Kann es zu diesem Graphen mehrere topologische Folgen geben? Ja
Wenn es mehrere topologische Folgen gibt, dann gib mindestens zwei dieser topologischen Folgen
an.
3. Aufgabe
Gegeben ist der folgende azyklische, gerichtete Graph
1
3
2
4
5
6
7
a) Zeige, wie sich dieser Graph durch eine Adjazensliste beschreiben lässt.
2
b) Für das topologische Sortieren ist die Aufnahme eines Zählers in der Knotenbeschreibung
zweckmäßig. Der Zähler soll festhalten, wie viele unmittelbare Vorgänger der Knoten hat. Hat ein
Knoten keine Vorgänger, dann wird der Zähler auf 0 gesetzt.
Trage in die Darstellung des Graphen zu den Knotennummern den Zähler für den unmittelbaren
Vorgänger des betreffenden Knoten ein.
c) Es ist zweckmäßig diesen Zähler in die Beschreibung der Knoten in einem Java-Programm, das die
topologische Folge der Knoten in einem Graphen ermittelt, aufzunehmen. Gib eine Beschreibung einer
Klasse Vertex an, die in Anlehnung an die Klasse Vertex im Skriptum1 die Knoten eines
azyklischen, gewichteten Graphen beschreibt, der den Zähler der unmittelbaren Vorgängerknoten
vorsieht.
class Vertex
{
______________________________________________________________________
______________________________________________________________________
______________________________________________________________________
______________________________________________________________________
______________________________________________________________________
______________________________________________________________________
}
d) Für den Aufbau des Graphen soll die Klasse Graph aus dem Skriptum herangezogen werden.
Ändere diese Vorlage so ab, daß der Graph über eine Adjazensliste eindeutig beschrieben wird und je
Knoten die Anzahl der Vorgängerknoten festgelegt wird. Hat ein Knoten keine Vorgänger, dann wird
dieser Zähler auf 0 gesetzt.
Gib die Veränderungen der Methode addEdge() bzw. getVertex() an, die den Aufbau des
Graphen, wie es soeben beschrieben wurde, erreichen
public void addEdge(String sourceName,String destName)
{
____________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
}
1
Vgl. pr22850 bzw. Skriptum, Kapitel 2
3
// Falls vertexName nicht da ist, fuege den Knoten
// mit diesem Namen in die vertexMap.
// In jedem Fall: Rueckgabe des Knoten.
private Vertex getVertex(String vertexName)
{
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
}
e) Den Kern der Klasse Graph bildet die Methode topsort(). Diese Methode ist durch die folgende
Pseudocode-Darstellung gegeben:
void topsort()
{
Queue q
int zaehler = 0;
Vertex v, w;
Q = new Queue();
for each vertex v
if (v.indegree2 == 0)
q = new Queue();
while (!q.isEmpty())
{
v = q.dequeue();
zaehler++;
for each w adjacent to v
if (--w.indegree == 0)
q.enqueue(w);
}
if (zaehler != anzahlKnoten)
System.out.println(“Fehler: Zyklus gefunden”);
}
Zur Bestimmung der gewünschten topologischen Folge wird mit den Knotenpunktnummern begonnen,
deren Zähler den Wert 0 enthalten. Sie verfügen über keinen Vorgänger und erscheinen in der
topologischen Folge an erster Stelle.
Überführe die Pseudocode-Darstellung der Methode topsort() in Java-Quellcode und baue diese
Methode in die Klasse Graph ein.
public void topsort()
{
_______________________________________________________________
_______________________________________________________________
2
indegree ist der Zähler für die jeweilige Anzahl von Vorgängerknoten
4
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
_______________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
5
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
__________________________________________________________________
}
f) Schreibtischtest. Die folgende Tabelle soll die Veränderung des Zählers für unmittelbare Vorgänger
zeigen und über die Knotenidentifikationen das Ein- bzw. Ausgliedern aus der Schlange (Queue) q.
Gib mit den Daten des in Aufgabe 3 vorgebenen Graphen die Tabelle an!
Vertex
1
2
3
4
5
6
7
Enqueue
Dequeue
1
2
3
4
5
6
7
g) Übersetze und teste das Programm
Lösungen
1. Aufgabe
a) Gegeben ist die binäre Relation R = {(1,2),(1,3),(2,4),(2,5),(2,6),(3,5),(3,7),(5,7),(6,7)}.
Gib an, wie sich diese binäre Relation durch einen gerichteten Graphen (anschaulich) darstellen lässt.
1
2
3
5
4
6
7
Die Relation beschreibt über die paarweisen Angaben Knotenidentifikationen. Ein Paar besteht aus
einem Startknoten und ein Zielknoten, im Graphen wird dies durch einen Pfeil vom Startknoten zum
Zielknoten beschrieben.
6
Pfeile im Graphen bestimmen die Ordnungsrelation (topologische Sortierung). Die dadurch bestimmte
Ordnung kann in eine lineare Ordnung eingebettet werden.
b) Zeige diese lineare Ordnung (topologische Sortierung) durch Anordnung der Knoten in einer Reihe,
so dass die Pfeile nach rechts zeigen.
1
2
4
6
3
5
7
d) Wie kann man die unter b) angegebene Folge einfach auf der Konsole (über ein Rechenprogramm)
darstellen?
1
2
4
5
6
3
5
7
Für azyklische Graphen kann man die Knoten-Identifikationen als Folge beschreiben. Bezogen auf
eine Kante (Pfeil) mit Anfangsknoten-Identifikation I und Endknoten-Identifikation J, erscheint die
Knoten-Identifikation I vor Knotenidentifikation J.
2. Aufgabe
Gegeben ist der folgende azyklische, gerichtete Graph
7
2
1
4
3
Kann es zu diesem Graphen mehrere topologische Folgen geben? Ja
Wenn es mehrere topologische Folgen gibt, dann gib mindestens zwei dieser topologischen Folgen
an.
1
2
1
3
3
4
2
4
3. Aufgabe
Gegeben ist der folgende azyklische, gerichtete Graph
0
1
1
2
2
3
3
1
4
5
3
2
6
7
a) Zeige, wie sich dieser Graph durch eine Adjazensliste beschreiben lässt.
1
2
3
2
4
5
3
6
4
6
7
5
4
7
4
3
6
8
b) Für das topologische Sortieren ist die Aufnahme eines Zählers in der Knotenbeschreibung
zweckmäßig. Der Zähler soll festhalten, wie viele unmittelbare Vorgänger der Knoten hat. Hat ein
Knoten keine Vorgänger, dann wird der Zähler auf 0 gesetzt.
Trage in die Darstellung des Graphen zu den Knotennummern den Zähler für den unmittelbaren
Vorgänger des betreffenden Knoten ein.
c) Es ist zweckmäßig diesen Zähler in die Beschreibung der Knoten in einem Java-Programm, das die
topologische Folge der Knoten in einem Graphen ermittelt, aufzunehmen. Gib eine Beschreibung einer
Klasse Vertex an, die in Anlehnung an die Klasse Vertex im Skriptum3 die Knoten eines
azyklischen, gewichteten Graphen beschreibt, der den Zähler der unmittelbaren Vorgängerknoten
vorsieht.
class Vertex
{
String
name;
LinkedList adj;
int
// Name des Knoten
// Benachbarte Knoten
indegree = 0; // Ingrad des Knoten
// Konstruktor
public Vertex( String nm )
{ name = nm; adj = new LinkedList(); }
}
d) Für den Aufbau des Graphen soll die Klasse Graph aus dem Skriptum herangezogen werden.
Ändere diese Vorlage so ab, daß der Graph über eine Adjazensliste eindeutig beschrieben wird und je
Knoten die Anzahl der Vorgängerknoten festgelegt wird. Hat ein Knoten keine Vorgänger, dann wird
dieser Zähler auf 0 gesetzt.
Gib die Veränderungen der Methode addEdge() bzw. getVertex() an, die den Aufbau des
Graphen, wie es soeben beschrieben wurde, erreichen
public void addEdge(String sourceName,String destName)
{
Vertex v = getVertex(sourceName);
Vertex w = getVertex(destName);
w.indegree++;
v.adj.add(w);
}
// Falls vertexName nicht da ist, fuege den Knoten
// mit diesem Namen in die vertexMap.
// In jedem Fall: Rueckgabe des Knoten.
private Vertex getVertex(String vertexName)
{
Vertex v = (Vertex) vertexMap.get(vertexName);
3
Vgl. pr22850 bzw. Skriptum, Kapitel 2
9
if( v == null )
{
v = new Vertex(vertexName);
vertexMap.put(vertexName, v);
}
return v;
}
e) Den Kern der Klasse Graph bildet die Methode topsort(). Diese Methode ist durch die folgende
Pseudocode-Darstellung gegeben:
void topsort()
{
Queue q;
int zaehler = 0;
Vertex v, w;
Q = new Queue();
for each vertex v
if (v.indegree4 == 0)
q = new Queue();
while (!q.isEmpty())
{
v = q.dequeue();
zaehler++;
for each w adjacent to v
if (--w.indegree == 0)
q.enqueue(w);
}
if (zaehler != anzahlKnoten)
System.out.println(“Fehler: Zyklus gefunden”);
}
Zur Bestimmung der gewünschten topologischen Folge wird mit den Knotenpunktnummern begonnen,
deren Zähler den Wert 0 enthalten. Sie verfügen über keinen Vorgänger und erscheinen in der
topologischen Folge an erster Stelle.
Überführe die Pseudocode-Darstellung der Methode topsort() in Java-Quellcode und baue diese
Methode in die Klasse Graph ein.
public void topsort()
{
Vertex v = null, w;
int zaehler = 0;
// Schlange fuer breadth search first
LinkedList q = new LinkedList( );
for (Iterator itr = vertexMap.values().iterator(); itr.hasNext();)
{
v = ((Vertex) itr.next());
4
indegree ist der Zähler für die jeweilige Anzahl von Vorgängerknoten
10
if (v.indegree == 0)
{
// enqueue: Einreihen in die Schlange
q.addLast(v);
// System.out.println(v.name);
}
}
if (v == null)
{
System.out.println("Fehler: Kein vorgaengerloser Knoten");
System.exit(0);
}
while( !q.isEmpty( ) )
{
// dequeue: Entnehmen aus der Schlange
v = (Vertex) q.removeFirst( );
zaehler++;
System.out.print(v.name + " ");
for( Iterator itr = v.adj.iterator( ); itr.hasNext( ); )
{
w = (Vertex) itr.next( );
if(--w.indegree == 0)
{
q.addLast( w );
}
}
}
// System.out.println(vertexMap.size());
// System.out.println(zaehler);
if (zaehler != vertexMap.size())
{
System.out.println("Fehler: Zyklus gefunden");
System.exit(0);
}
11
}
f) Schreibtischtest. Die folgende Tabelle soll die Veränderung des Zählers für unmittelbare Vorgänger
zeigen und über die Knotenidentifikationen das Ein- bzw. Ausgliedern aus der Schlange (Queue) q.
Gib mit den Daten des in Aufgabe 3 vorgebenen Graphen die Tabelle an!
Vertex
1
2
3
4
5
6
7
Enqueue
Dequeue
1
0
1
2
3
1
3
2
1
1
2
0
0
1
2
1
3
2
2
2
3
0
0
1
1
0
3
2
5
5
4
0
0
1
0
0
3
1
4
4
5
0
0
0
0
0
2
0
3,7
3
6
0
0
0
0
0
1
0
7
7
0
0
0
0
0
0
0
6
6
g) Übersetze und teste das Programm
import java.util.*;
import java.io.*;
class Vertex
{
String
name;
// Name des Knoten
LinkedList adj;
// Benachbarte Knoten
int
indegree = 0; // Ingrad des Knoten
// int
dist;
// Kosten
// Vertex
path;
// Vorheriger Knoten auf dem kuerzesten Pfad
// Konstruktor
public Vertex( String nm )
{ name = nm; adj = new LinkedList(); }
}
public class Graph
{
// Abbildung der Knoten
private HashMap vertexMap = new HashMap();
// Methode Hinzufuegen Kante
public void addEdge(String sourceName,String destName)
{
Vertex v = getVertex(sourceName);
Vertex w = getVertex(destName);
w.indegree++;
v.adj.add(w);
}
// Falls vertexName nicht da ist, fuege den Knoten
// mit diesem Namen in die vertexMap.
// In jedem Fall: Rueckgabe des Knoten.
private Vertex getVertex(String vertexName)
{
Vertex v = (Vertex) vertexMap.get(vertexName);
if( v == null )
{
v = new Vertex(vertexName);
vertexMap.put(vertexName, v);
}
return v;
}
/*
* Herstellung der Ordnung von Knoten in einem
* gerichteten, azyklischen Graphen
12
*/
public void topsort()
{
Vertex v = null, w;
int zaehler = 0;
// Schlange fuer breadth search first
LinkedList q = new LinkedList( );
for (Iterator itr = vertexMap.values().iterator(); itr.hasNext();)
{
v = ((Vertex) itr.next());
if (v.indegree == 0)
{
// enqueue: Einreihen in die Schlange
q.addLast(v);
// System.out.println(v.name);
}
}
if (v == null)
{
System.out.println("Fehler: Kein vorgaengerloser Knoten");
System.exit(0);
}
while( !q.isEmpty( ) )
{
// dequeue: Entnehmen aus der Schlange
v = (Vertex) q.removeFirst( );
zaehler++;
System.out.print(v.name + " ");
for( Iterator itr = v.adj.iterator( ); itr.hasNext( ); )
{
w = (Vertex) itr.next( );
if(--w.indegree == 0)
{
q.addLast( w );
}
}
}
// System.out.println(vertexMap.size());
// System.out.println(zaehler);
if (zaehler != vertexMap.size())
{
System.out.println("Fehler: Zyklus gefunden");
System.exit(0);
}
}
/*
* Eine main()-Routine, die
* 1. Eine Datei liest, die Kanten enthaelt
*
(Der Dateiname wird als Parameter ueber die
*
Kommandozeile eingegeben);
* 2. den Graphen aufbaut;
* 3. wiederholt 2 Knoten anfordert und
*
den Algorithmus zur Berechnung des topologischen Sort
*
in Gang setzt.
* Die Datei besteht aus Zeilen mit dem Format
*
Quelle (source) Ziel (destination).
*/
public static void main(String [] args)
{
Graph g = new Graph( );
try
{
FileReader din = new FileReader(args[0]);
BufferedReader graphFile = new BufferedReader(din);
// Lies die Kanten und fuege ein
String zeile;
while( ( zeile = graphFile.readLine() ) != null )
13
{
StringTokenizer st = new StringTokenizer(zeile);
try
{
if( st.countTokens( ) != 2 )
throw new Exception( );
String source = st.nextToken( );
String dest
= st.nextToken( );
g.addEdge(source, dest);
}
catch( Exception e )
{ System.err.println( e + " " + zeile ); }
}
}
catch( Exception e )
{ System.err.println( e ); }
System.out.println( "File read" );
// System.out.print(g.vertexMap);
// System.out.println(g.vertexMap.size());
g.topsort();
System.out.println();
}
}
14
Herunterladen