Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus:...

35

Transcript of Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus:...

Page 1: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.
Page 2: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Wir müssen also überlegen:• Implementierung der Knoten,• Implementierung der Kanten,• daraus: Implementierung des Graphen insgesamt.

Annahme: die Knoteninhalte sind Zeichenketten(Vereinfachung - Knoteninhalte können beliebigkomplex sein).

Zunächst: Operationen auf Knoten

Page 3: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Prinzip: Information Hiding (Verbergen von Informationen).

Allgemein: Information sollten von Benutzern einer Struktur nur gelesen werden können. Änderungen sollten durch Funktions-

aufrufe kontrollierbar sein.

Grund: Programme als Grund: Programme als KontraktKontrakt• zwischen Aufrufern und Akteuren, zwischen Aufrufern und Akteuren, • die Aufrufer garantieren Eigenschaften von Werten,die Aufrufer garantieren Eigenschaften von Werten,• die Akteure garantieren wohldefiniertes Verhalten,die Akteure garantieren wohldefiniertes Verhalten,Das geht nur durch Kontrolle des Zugriffsverhaltens. Das geht nur durch Kontrolle des Zugriffsverhaltens.

Page 4: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Konsequenzen:• Daten in einer struct möglichst private• Zugriff: lesend und schreibend durch geeignete Funktionen.

struct Bsp {int a;char b;};

struct Bsp {int a;char b;};

Es sei deklariert: Bsp X;

X.a = 1 und cout << X.a sind möglich, alsoschreibender und lesender Zugriff.

Page 5: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Insbesondere kann jedes Programm, das auf X zugreifen kann, X.a ändern, ohne daß X selbst davonerfährt.

struct Bsp2 {private:

int a;public:

Setze(int);int

Lese();char b;

};

struct Bsp2 {private:

int a;public:

Setze(int);int

Lese();char b;

};

Deklariere Bsp2 YBsp2 Y;

Y.aY.a ist illegalund wird vom Compilerzurückgewiesen.

Page 6: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

struct Bsp2 {private:

int a;public:

void Setze(int);

int Lese();char b;

};

void Bsp2::Setze(int x) {a = x;

}

int Bsp2::Lese() {return a;

}

struct Bsp2 {private:

int a;public:

void Setze(int);

int Lese();char b;

};

void Bsp2::Setze(int x) {a = x;

}

int Bsp2::Lese() {return a;

}

Bsp2 Ysei deklariert.• Y.aY.a ist ein illegaler Ausdruck,• Y.bY.b dagegen nicht,• Zugriff auf Y.aY.a lesend nur durch Y.Lese(),Y.Lese(), schreibend (also verändernd) nur durch Y.Setze(33).Y.Setze(33).

Page 7: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Konsequenz:• wenn die Inhalte der Knoten des Graphen nicht von außen (d.h. durch beliebige Benutzer) geändert werden sollen, so sollte der Inhalt als private deklariert werden.• Weiterhin sind Definitions- und Leseoperationen notwendig.

struct Knoten {struct Knoten {private:private:

char * Inhalt;char * Inhalt;........

public:public:void SetzeWert(char *);void SetzeWert(char *);char * LeseWert();char * LeseWert();........

};};

Inhalt: Zeichenketteunbestimmter Länge

WeitereFelder

Page 8: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

void Knoten::SetzeWert(char *s) {void Knoten::SetzeWert(char *s) {for (int j = 0; s[j++] != '\0'; );for (int j = 0; s[j++] != '\0'; );Inhalt = Inhalt = newnew char [--j]; char [--j];Inhalt = s;Inhalt = s;......}}

char * Knoten::LeseWert() {return Inhalt;}char * Knoten::LeseWert() {return Inhalt;}

Stelle Längeder Zeichenkettefest

Verschaffe mir einentsprechend großes Feld.

Zuweisung

Page 9: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

int Gleich(Knoten *);int Gleich(Knoten *);

Weitere Operationen:

Gegeben ein Knoten, möchte feststellen, ob seinInhalt gleich dem des aktuellen Knotens ist:

int Knoten::Gleich(Knoten * K) {int Knoten::Gleich(Knoten * K) {int strcmp(char *, char *);int strcmp(char *, char *);return (strcmp((*K).Inhalt, Inhalt) == 0? return (strcmp((*K).Inhalt, Inhalt) == 0?

true: false);true: false);}}

Die üblichen Konstanten

AlterBekannter

Page 10: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Die Adjazenzlisten zu einem Knoten werdenin einer verketteten Liste verwaltet. Ergibt ein private Attribut Adjazenz mit entsprechenden Funktionen:

struct Knoten {struct Knoten {private:private:

char * Inhalt;char * Inhalt;Knoten * AdjazenzKnoten * Adjazenz;;......

public:public:void SetzeWert(char *);void SetzeWert(char *);char * LeseWert();char * LeseWert();int Gleich(Knoten *);int Gleich(Knoten *);......Knoten * LeseAdjazenz();Knoten * LeseAdjazenz();void SetzeAdjazenz(Knoten *);void SetzeAdjazenz(Knoten *);

};};

Page 11: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Knoten * Knoten::LeseAdjazenz() { Knoten * Knoten::LeseAdjazenz() { return Adjazenz;return Adjazenz;

}}

void Knoten::SetzeAdjazenz(Knoten * K) {void Knoten::SetzeAdjazenz(Knoten * K) {Knoten * L = new Knoten;Knoten * L = new Knoten;...;...;L->SetzeWert(K->LeseWert());L->SetzeWert(K->LeseWert());L->Adjazenz = Adjazenz;L->Adjazenz = Adjazenz;Adjazenz = L;Adjazenz = L;}}

Das sollte klar sein.

Page 12: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Erzeuge also einen neuen Knoten L

• kopiere die Zeichenkette von K (dem Parameter) nach L:

L->SetzeWert(K->LeseWert());L->SetzeWert(K->LeseWert());

• füge L an den Anfang der Adjazenzliste des Knotens L->Adjazenz = AdjazenzL->Adjazenz = Adjazenz

•das ist dann die Adjazenzliste des Knotens:Adjazenz = LAdjazenz = L

Page 13: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Schließlich werden die Knoten miteinander verkettet(Verwaltungsinformation, die nichts mit dem Graphenselbst zu tun hat).

InhaltInhaltAdjazenzAdjazenz-

liste*

AdjazenzAdjazenz-liste

*

Ver-Ver-waltungwaltung

**

Page 14: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

struct Knoten {struct Knoten {private:private:

char * Inhalt;char * Inhalt;Knoten * Adjazenz;Knoten * Adjazenz;Knoten * Verwaltung;Knoten * Verwaltung;

public:public:void SetzeWert(char *);void SetzeWert(char *);char * LeseWert();char * LeseWert();int Gleich(Knoten *);int Gleich(Knoten *);Knoten * LeseVerwaltung();Knoten * LeseVerwaltung();Knoten * LeseAdjazenz();Knoten * LeseAdjazenz();void SetzeVerwaltung(Knoten *);void SetzeVerwaltung(Knoten *);void SetzeAdjazenz(Knoten *);void SetzeAdjazenz(Knoten *);void Druck(char *, char *);void Druck(char *, char *);

};};

Vollständige Definition von Knoten:

Page 15: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Knoten * Knoten::LeseVerwaltung() {Knoten * Knoten::LeseVerwaltung() {return Verwaltung;return Verwaltung;

}}

void Knoten::SetzeVerwaltung(Knoten * K) {void Knoten::SetzeVerwaltung(Knoten * K) {K->Verwaltung = Verwaltung;K->Verwaltung = Verwaltung;Verwaltung = K;Verwaltung = K;}}

(ist wieder trivial)

Setze den neuen Knotenan den Anfang derVerwaltungsliste

Page 16: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Schließlich: DruckenDrucken

void Knoten::Druck(char * vor, char * nach) {void Knoten::Druck(char * vor, char * nach) {*ausgabe << vor << Inhalt << nach;*ausgabe << vor << Inhalt << nach;}}

Datei fürdie Ausgabe

Texte, in denen derInhaltInhalt eingebettetwerden kann (z. B.„„\t“\t“ oder „\n“„\n“)

Page 17: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Sozusagen Basisschicht im Schichtenmodell:

Definition von Knoten undihrer Grundoperationen

Definition des Graphen undseiner Grundoperationen

eine klitzekleine Zwischenschichteine klitzekleine Zwischenschicht

Page 18: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Zwischenschicht• dient zur Realisierung von hilfreichen Operationen auf Knoten des Graphen• Operationen sind freilich nicht so vital für die Knoten, daß sie in die Knoten-Definition aufhgenommen werden müßten.• es handelt sich dabei um:

• Finden eines Knoten in der Verwaltungsliste eines anderen,• Einfügen eines Knoten in dieser Liste,• Finden eines Knoten in der Adjazenzliste eines anderen,• Drucken der Adjazenzliste eines Knoten.

Page 19: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

int VerwFinde(Knoten *K, Knoten *L) {int VerwFinde(Knoten *K, Knoten *L) {if (L == NULL)if (L == NULL)

return false;return false;elseelse return (return (K->GleichK->Gleich(L) ? true: (L) ? true:

VerwFinde(K, VerwFinde(K, L->LeseVerwaltungL->LeseVerwaltung()));()));}}

Knoten * VerwEinfuegen(Knoten * K, Knoten * L){Knoten * VerwEinfuegen(Knoten * K, Knoten * L){int VerwFinde(Knoten *, Knoten *);int VerwFinde(Knoten *, Knoten *);if (!VerwFinde(K, L))if (!VerwFinde(K, L))

K->SetzeVerwaltungK->SetzeVerwaltung(L);(L);return K;return K;}}

Page 20: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

int AdjFinde(Knoten *K, Knoten *L) {int AdjFinde(Knoten *K, Knoten *L) {if (L == NULL) return false;if (L == NULL) return false;else return (else return (K->GleichK->Gleich(L) ? true: (L) ? true:

AdjFinde(K, AdjFinde(K, L->LeseAdjazenzL->LeseAdjazenz()));()));}}

void ListenDruck(Knoten *L) {void ListenDruck(Knoten *L) {if (L == NULL)if (L == NULL)

*ausgabe << "}," << endl;*ausgabe << "}," << endl;else {else {

*ausgabe << *ausgabe << L->LeseWertL->LeseWert() << () << ((L->LeseAdjazenzL->LeseAdjazenz() == NULL () == NULL ? "" : ", ");? "" : ", ");

ListenDruck(ListenDruck(L->LeseAdjazenzL->LeseAdjazenz());());}}

}}

Page 21: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Analoges Vorgehen bei der Formulierung des Graphen:• welche Komponenten sind privateprivate?• welche Operationen werden benötigt?• Hilfsoperationen wieder versteckt, damit zweifache Funktion der privateprivate-Deklaration:

• Schützen vor unberechtigtem Zugriff,• Verbergen von Funktionen, die nicht außerhalb eines wohldefinierten lokalen Kontexts aufgerufen werden sollten.

Page 22: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Der Graph besteht zunächst aus einem Zeiger aufeinen Knoten (ähnlich der Wurzel bei binärenSuchbäumen) und der Initialisierung:

struct Graph {struct Graph {private:private:

Knoten * EinKnoten;Knoten * EinKnoten;......

public:public:void Init();void Init();......

};};

Page 23: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Ein Knoten? (Ganz Gallien?)Über die Verwaltungsinformation werden alle anderenKnoten eingefügt.

void Graph::Init() {void Graph::Init() {EinKnoten = NULL;EinKnoten = NULL;

}}

Die Initialisierung ist ziemlich trivial:

Page 24: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Hilfsfunktion: Nachsehen, wo ein Knoten auf derVerwaltungsliste eines anderen steckt; ein Zeigerauf diesen Knoten wird zurückgegeben. Wird alsprivate vereinbart, da nur lokal verwendet.

Knoten * Graph::DaIstDerKnoten(Knoten * K, Knoten * Graph::DaIstDerKnoten(Knoten * K, Knoten * Wo) {Knoten * Wo) {

if (Wo == NULL) if (Wo == NULL) return NULL;return NULL; else if (else if (K->GleichK->Gleich(Wo) == true) (Wo) == true) return Wo;return Wo; else else return return

DaIstDerKnoten(K, DaIstDerKnoten(K, Wo->LeseVerwaltungWo->LeseVerwaltung());());

}}

Page 25: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Weiter: Testfunktionen• ist ein Knoten mit einem bestimmten Inhalt im Graphen vorhanden?• sind zwei Knoten mit einer Kante verbunden?

int Graph::KnotenTesten(Knoten * K) {int Graph::KnotenTesten(Knoten * K) {if (DaIstDerKnoten(K, EinKnoten) != NULL ?)if (DaIstDerKnoten(K, EinKnoten) != NULL ?)

return true;return true;else else

return false;return false;}}

Page 26: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

int Graph::KanteTesten(Knoten * K, Knoten * L) {int Graph::KanteTesten(Knoten * K, Knoten * L) {Knoten * OK = DaIstDerKnoten(K, EinKnoten);Knoten * OK = DaIstDerKnoten(K, EinKnoten);return (OK == NULL ? false : AdjFinde(OK, L));return (OK == NULL ? false : AdjFinde(OK, L));}}

Also:• suche den Knoten K in der Verwaltungsliste von EinKnoten,• ist der Knoten nicht vorhanden (d.h. wird NULL zurück- gegeben), kann keine Kante vorhanden sein,• sonst: suche in der Adjazenzliste des Resultatknotens nach dem Knoten L.

Page 27: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Stand der Dinge:

struct Graph {struct Graph {private:private:

Knoten * EinKnoten;Knoten * EinKnoten;Knoten * DaIstDerKnoten(Knoten *, Knoten * DaIstDerKnoten(Knoten *,

Knoten *);Knoten *);public:public:

void Init();void Init();int KnotenTesten(Knoten *);int KnotenTesten(Knoten *);int KanteTesten(Knoten *, Knoten *);int KanteTesten(Knoten *, Knoten *);......

};};

Page 28: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

void Graph::KnotenEinfuegen(Knoten * K) {void Graph::KnotenEinfuegen(Knoten * K) {if (EinKnoten == NULL) EinKnoten = K;if (EinKnoten == NULL) EinKnoten = K;else if (!KnotenTesten(K))else if (!KnotenTesten(K))

EinKnoten->SetzeVerwaltung(K);EinKnoten->SetzeVerwaltung(K);}}

Ziemlich kanonisch: Einfügen von Knoten und Kanten:

Sehe nach, ob überhaupt schon ein Knoten vorhanden ist.

Nur falls der Knoten nochnicht vorhanden ist, wird erin die Verwaltungslisteeingefügt.

Page 29: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

void Graph::KanteEinfuegen(Knoten *K, Knoten *L) {void Graph::KanteEinfuegen(Knoten *K, Knoten *L) {if (!KnotenTesten(K)) KnotenEinfuegen(K);if (!KnotenTesten(K)) KnotenEinfuegen(K);if (!KnotenTesten(L)) KnotenEinfuegen(L);if (!KnotenTesten(L)) KnotenEinfuegen(L);if (!KanteTesten(K, L)) {if (!KanteTesten(K, L)) {

K->SetzeAdjazenz(L); K->SetzeAdjazenz(L); L->SetzeAdjazenz(K);L->SetzeAdjazenz(K);

}}}}

Einfügen einer Kante zwischen zwei Knoten:

Schaue nach, ob dieKnoten überhaupt schonvorhanden sind, füge sieggf. ein.

Überprüfe, ob dieKante bereits vorhandenist, sonst setze dieAdjazenzfelder beiderbeiderKnoten entsprechend.

Page 30: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Schließlich, als Hilfsfunktionen:• Konstruiere aus einer Zeichenkette einen neuen Knoten im Graphen (MkKnoten),• Druck des gesamten Graphen.

Page 31: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Knoten * Graph::MkKnoten(char * s) {Knoten * Graph::MkKnoten(char * s) {Knoten * K = new Knoten, * L;Knoten * K = new Knoten, * L;K->SetzeWert(s);K->SetzeWert(s);L = DaIstDerKnoten(K, EinKnoten);L = DaIstDerKnoten(K, EinKnoten);return (L == NULL ? K : L);return (L == NULL ? K : L);}}

Konstruiere einenKnoten mit demInhalt-Feld

Schaue nach, ob ein Knotenmit diesem Inhalt bereitsim Graphen vorhanden ist

Setze Rückgabewert entsprechend

Page 32: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Drucken: • drucke jeden Knoten,• drucke für jeden Knoten seine Adjazenzliste (kommt aus der Zwischenschicht).

void Graph::Druck(char * vor, char * nach) {void Graph::Druck(char * vor, char * nach) {void ListenDruck(Knoten *);void ListenDruck(Knoten *);for (Knoten * K = EinKnoten; K != NULL; for (Knoten * K = EinKnoten; K != NULL;

K = K->LeseVerwaltung()) {K = K->LeseVerwaltung()) {*ausgabe << "ADJ("; K->Druck(vor, nach);*ausgabe << "ADJ("; K->Druck(vor, nach);ListenDruck(K->LeseAdjazenz());ListenDruck(K->LeseAdjazenz());}}

}}

Page 33: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Insgesamt sieht die struct so aus:

struct Graph {struct Graph {private:private:

Knoten * EinKnoten;Knoten * EinKnoten;Knoten * DaIstDerKnoten(Knoten *, Knoten * DaIstDerKnoten(Knoten *,

Knoten *);Knoten *);public:public:

void Init();void Init();Knoten * MkKnoten(char *);Knoten * MkKnoten(char *);int KnotenTesten(Knoten *);int KnotenTesten(Knoten *);int KanteTesten(Knoten *, Knoten *);int KanteTesten(Knoten *, Knoten *);void KnotenEinfuegen(Knoten *);void KnotenEinfuegen(Knoten *);void KanteEinfuegen(Knoten *, Knoten *);void KanteEinfuegen(Knoten *, Knoten *);void Druck(char *, char *);void Druck(char *, char *);

};};

Page 34: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

void main() {void main() {Graph * G = new Graph;Graph * G = new Graph;char * ara[] = {"\t", "eins", "zwei", "drei", char * ara[] = {"\t", "eins", "zwei", "drei",

"vier","vier", "fünf", "sechs", "sieben"};"fünf", "sechs", "sieben"};Knoten * MkKnoten(char *), * K[8];Knoten * MkKnoten(char *), * K[8];

G->Init();G->Init();for (int i = 1; i < 8; i++) for (int i = 1; i < 8; i++)

K[i] = G->MkKnoten(ara[i]);K[i] = G->MkKnoten(ara[i]);

G->KanteEinfuegen(K[1], K[2]);G->KanteEinfuegen(K[1], K[2]); ... ... G->KanteEinfuegen(K[6], K[7]);G->KanteEinfuegen(K[6], K[7]); G->Druck("", ") = {");G->Druck("", ") = {");}}

(Beispielgraph)

Page 35: Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.

Ausgabe:

ADJ(eins) = {sieben, sechs, zwei},ADJ(eins) = {sieben, sechs, zwei},ADJ(sieben) = {sechs, fünf, vier, drei, zwei, eins},ADJ(sieben) = {sechs, fünf, vier, drei, zwei, eins},ADJ(sechs) = {sieben, eins, fünf},ADJ(sechs) = {sieben, eins, fünf},ADJ(fünf) = {sieben, sechs, vier},ADJ(fünf) = {sieben, sechs, vier},ADJ(vier) = {sieben, fünf, drei},ADJ(vier) = {sieben, fünf, drei},ADJ(drei) = {sieben, vier, zwei},ADJ(drei) = {sieben, vier, zwei},ADJ(zwei) = {sieben, drei, eins},ADJ(zwei) = {sieben, drei, eins},

Programm: prog-25.cpp