[C] Baum mit eigenen Datentyp erstellen

neuesich

Mitglied
Hallo,

Ich habe die Aufgabe einen Baum in C abzubilden.
Um zu verdeutlichen wie die Struktur eingelesen wird hier ein Beispiel:
Code:
1:A;
2:B;
3:C;
4:E;
5:F;
6:D;
7:E;
8:F;
9:D;
1->2;
1->3;
1->4;
1->5;
2->6;
2->7;
3->8;
3->9;
dies soll folgende Grafik erzeugen:
Zeichnung1.png
Dabei gilt folgendes: Jeder Knoten hat einen Namen von A-Z (Großbuchstaben) und dazu eine eindeutige ID von 1 bis 10000.

Jeder Knoten wird durch den Datentyp graph abgebildet. Da jeder Knoten n Kinder haben kann enthält der Datentyp eine Liste, die auf die Kinder verweisen soll.

Um eine Verknüpfung aus dem String zu erstellen werden graph-Elemente erzeugt und in eine Liste gepackt (tmpls). Dann wird bei einer Zuweisung auf die liste zurückgegriffen und die Verknüpfung geschaffen.

Leider läuft da irgendwas schief und ich kann den Fehler nicht finden. Ich vermute den Fehler in der addChildToNode aber kann ihn nicht entdecken.

Bei mir wird immer folgende Struktur erzeugt: Knoten A mit den Kindern E und F (ohne weitere Kinder)

Hier mein bisheriger Code:
C++:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>    //C99-Standard
#include <strings.h>    //strtok()

#define DELIMITER ";"                //Trennzeichen für Operationen

typedef struct nextelement{             //Linkls: Kind-Liste innerhalb eines Knotens
            struct node *child;         //      - child: Zeiger auf Kindelement des Knotens
            struct nextelement *next;   //      - nextelement: Zeiger auf nächstes Listenelement
        }linkls;

typedef struct node{            //graph: Ein Knotenpunkt des Graphs
        char label;             //      - label: Name des Knotens, doppeltes Vorkommen möglich
        int id;                 //      - id: eindeutige Nummer des Knotens
        linkls *link;           //      - link: Startpunkt der Liste der Kinder-Elemente
    }graph;

typedef struct lsele{           //tmpls: Temporäre Liste zum Erzeugen der Verknüpfungen der Struktur
        graph *ele;             //       - ele: Knotenpunkt, der nach Erstellen festgehalten wird
        struct lsele *nxt;      //       - nxt: Nächstes Listenelement
        }tmpls;

/** Funktion getAddressFromList
 *   Durchläuft temporäre Liste für das Verknüpfen der erzeugten Elemente.
 *   Nach dem Auffinden einer gesuchten ID wird das Element zurückgegeben.
 * IN: - list: Zeiger auf das Anfangselement der Liste
 *     - id: Gesuchte ID des zu verknüpfenden Elements
 * RETURN: Pointer auf gesuchtes Graphenelement
 */
graph *getAddressFromList(tmpls *list, int id){
    tmpls *runner=list;
    if(!runner)                  //Wenn Liste leer->NULL
        return NULL;

    //Ein Element weitergehen so lange es ein nächstes Element gibt und die gesuchte ID nicht gefunden wurde
    while((id!=runner->ele->id) && (runner->nxt))
        runner=runner->nxt;

    if(id==runner->ele->id)     //Wenn aktuelles Element die ID hat -> zurückgeben
        return runner->ele;
    else
        return NULL;            //Andernfalls nicht in Liste gefunden
}


/** Funktion appendToList
 *   Läuft bis ans Ende der temporären Liste und hängt neues Element an
 * IN: - element: Knoten der ans Ende der Liste gespeichert werden soll
 *     - list: Pointer auf Pointer auf Liste (Änderbarkeit bei leerer Liste)
 */
void appendToList(graph *element, tmpls **list){
    tmpls *runner=*list,
          *newelement=(tmpls *) malloc (sizeof(tmpls));

    if(newelement){                    //Wenn Speicher reserviert werden konnte
        newelement->ele=element;       //Übergebenen Knoten dem Listenelement zuordnen
        newelement->nxt=NULL;

        if(!(*list))                   //Wenn Liste leer -> Element ist Liste
            *list=newelement;

        else{
            while (runner->nxt)        //Andernfalls ans Ende der Liste gehen
                runner=runner->nxt;
            runner->nxt=newelement;    //und dann Listenelement anhängen
        }
    }
    else
        printf("Es konnte kein Speicher freigegeben werden!\n");
}

/** Funktion removeList
 *   Gibt den reservierten Speicher für die temporäre Liste frei
 * IN: - element: Knoten der ans Ende der Liste gespeichert werden soll
 *     - list: Pointer auf Pointer auf Liste (Änderbarkeit bei leerer Liste)
 */
void removeList(tmpls *start){
    tmpls *deleter, *tmp;
    if(start){                      //Wenn Liste nicht leer
        deleter=start->nxt;         //Setze deleter an 2. Position
        while(deleter){            //So lange noch ab der zweiten Position Elemente in der Liste bestehen
            tmp=start->nxt->nxt;   //Gehe mit tmp auf 3. Position
            start->nxt=tmp;        //Startelement zeigt auf 3.Position (Ausgliederung des 2. Elements)
            free(deleter);         //Zweites Element wird gelöscht
            deleter=tmp;           //deleter wird wieder auf 2.Position gesetzt
        }
        free(start->nxt);          //Zweites Element wird gelöscht
        free(start);               //Startelement wird gelöscht
        start=NULL;
    }
}

/** Funktion createNewNode
 *   Reserviert Speicherplatz für ein neues Element und ordnet ihm Werte zu
 * IN: - id: Eindeutige Nummer des Knotens
 *     - label: Großbuchstabe, der den Namen des Knotens darstellt
 * RETURN: Neu erzeugter Knotenpunkt
 */
graph *createNewNode(int id, char label){
    graph *newelement=(graph*) malloc (sizeof(graph));

    if(!newelement)             //Bei Fehlschlagen->NULL
        return NULL;
    newelement->id=id;
    newelement->label=label;
    newelement->visited=false;
    newelement->link=NULL;
    printf("Nummer %i mit Label %c wurde auf Adresse %p definiert\n", id, label, newelement);
    return newelement;
}

/** Funktion addChildToNode
 *   Ordnet einem Knoten einen anderen Knoten als Kind zu
 * IN: - node: Eltern-Knoten der auf etwas zeigen soll
 *     - newchild: Kind-Knoten den es zu verknüpfen gilt
 */
void addChildToNode(graph *node, graph *newchild){
    linkls *linkele=(linkls *) malloc (sizeof(linkls));

    if(!linkele)
        printf("Erstellen des Elements fehlgeschlagen: Kein freier Speicher verfügbar!\n");
    linkele->next=NULL;
    linkele->child=newchild;

    if(node->link){    // Wenn bereits eins oder mehrere Kind-Elemente bestehen
        while(node->link->next)
            node->link=node->link->next;
        node->link->next=linkele;
    }
    else{               // Fall, dass noch keine Kind-Liste zugewiesen wurde
        node->link=linkele;
    }
}

/** Funktion createStructure
 *   Erzeugt Datenstruktur aus String
 * IN: - input: Konstrukt der eingelesenen Graphendatei
 * RETURN: gibt Wurzelknoten des erzeugten Graphs zurück
 */
graph *createStructure(char *input){
    graph *root=NULL;
    tmpls *list=NULL;
    int i=-1,o1=-1,o2=-1;
    char c='0',
    *tmp=strtok(input, DELIMITER);      //erstes "Teilen" des Strings

    //Definition des ersten Punktes, der den Startpunkt (Wurzel) darstellt
    if ((sscanf(tmp, "%i:%c", &i, &c) == 2) && (c>=65) && (c<=90) && (i>0) && (i<10000)){
        root=createNewNode(i,c);        //Startpunkt des Graph erzeugen, festhalten
        appendToList(root, &list);      // und dann in Liste packen
        tmp=strtok(NULL, DELIMITER);    //String erneut teilen
    }
    else
        printf("Fehlerhafte Definition des Wurzelelements! Einlesen wird abgebrochen.\n");

    while(tmp){                 //So lange String noch nicht abgearbeitet ist
        // Definitionsoperation: GANZZAHL:GROßBUCHSTABE //
	    if ((sscanf(tmp, "%i:%c", &i, &c) == 2) && (c>=65) && (c<=90) && (i>0) && (i<10000)){
            appendToList(createNewNode(i,c), &list);       //neu erzeugten Knoten in Liste hängen
        }
        // Zuweisungsoperation: GROßBUCHSTABE->GROßBUCHSTABE//
        else if((sscanf(tmp, "%i->%i", &o1, &o2) == 2) && (o1>0) && (o1<10000) && (o2>0) && (o2<10000)){
            addChildToNode(getAddressFromList(list,o1),getAddressFromList(list,o2)); //Beide Knoten aus Liste holen und einander zuordnen
            printf("%i zeigt auf %i (%p->%p)\n", o1,o2,getAddressFromList(list,o1),getAddressFromList(list,o2));
        }
        else{  // Fehlerhafte Syntax->Abbruch
            printf("Fehlerhafter Dateiinhalt: Syntaxfehler! Das Einlesen wird abgebrochen.\n");
            return NULL;
        }
 	tmp = strtok(NULL, DELIMITER);    //fortführendes Teilen des Strings
    }

    removeList(list);       //Am Ende: Temporäre Liste auflösen
    return root;            // und Wurzelelement zurückgeben
}

void main(void){
    graph *gstructure=NULL;
    char graphdata[]="1:A;2:B;3:C;4:E;5:F;6:D;7:E;8:F;9:D;1->2;1->3;1->4;1->5;2->6;2->7;3->8;3->9;";

    gstructure=createStructure(graphdata);
}
Ich freue mich über jede Anregung, Kritik und Hilfe! Danke im Voraus!
 
Also das einzige was zu sagen wäre ist, in Zeile 108 setzt du visited auf false, es gibt jedoch in graph keinen Member, der visited heißt. Ansonsten funktioniert das Programm einwandfrei, wenn ich es ausführe (jedenfalls dem Output zufolge passiert genau die Verkettung, die in deinem Bild beschrieben wird).

Lg
 
Naja die printfs aus Zeile 109 und 165 geben zwar aus, dass die Operation erfolgreich ausgeführt wurde, aber wie immer zählt ja nur das Endresultat^^

Einfach mal ans Ende der main:
C++:
    printf("Das Wurzelelement A hat die folgenden Kinder:\n");
    if(gstructure->link)
        while(gstructure->link){
            printf("%c (id:%d)\n", gstructure->link->child->label, gstructure->link->child->id);
            gstructure->link=gstructure->link->next;
        }
Dann sieht man, dass bei der Zuweisung was schief gelaufen ist. Nur was:confused:
 
Könntest du bitte nochmal genau sagen, was schief läuft, weil ich ansonsten gar nicht weiß, wo ich mit dem Suchen anfangen soll. :)
 
Stimmt, man sollte sich doch nicht immer auf den Output verlassen ;)

Aber zurück zum Problem:
Du machst zwar alles richtig, nur in der while-Schleife in Zeile 128 veränderst du den Wert, auf den der Child-Zeiger von A zeigt. In Zeile 129 steht node->link = ... Du darfst diesen Zeiger nicht verändern, der zeigt ja auf das erste Child von A, und am Ende der Funktion zeigt er aber auf das vorletzte Child und er wird nicht mehr zurückgesetzt.
Du brauchst nur einen eigenen Pointer für die while-Schleife erstellen, der die Child-Liste durchläuft und es wird funktionieren.

Lg
 
Danke ibafluss, natürlich^^
Ich hab ewig davor gesessen und den Fehler nicht gesehen. Mehrere Augen sehen einfach mehr...
Nochmal vielen Dank an euch Beide;)
 
Zurück