BinaerBaeume unter C "richtig" ausgeben...



  • einfach show_tree noch einen parameter x und y mitgeben, wenn du eine ebene tiefer gehst, also show_tree aufrufst, muss y größer werden. beim show_tree-aufruf für links muss x wieder etwas kleiner als das übergebene x sein und beim show_tree-aufgruf für rechts halt größer... sollte man grafisch irgendwie lösen, rein konsolen/textbasiert ist das nicht so einfach, brauch man ne menge ausgabe-formatierungen -> umständlich



  • wofür brauchst du parent ?



  • nur für show_tree ist es sinnlos, aber ansonsten für baumstrukturen unabdingbar, weil es mit sicherheit vorkommt, dass du das eine oder andere mal auch den baum von unten hochsteigen musst...



  • Verstehe ich nicht ganz, da ja normalerweise vom Rootnode aus rekursive gearbeitet wird?



  • schon klar, aber wenn du z.b. ein node löschen willst und nicht vom root ausgehst (weil den die funktion z.b. nicht hat), dann ist parent ziemlich nützlich 🙂



  • Wo willst du den Bauzm ausgeben Screen, Drucker, File?
    Das einfachste ist File in ASCII.
    den Baum durchlaufen das tiefste Element = größter Abstand zu Rootbestimmen
    Im Baum das äußerst linke Element aufsuchen
    von dort beginnend Blatt und Ast ablaufen und in die Datei drucken.
    File:

    Nodename
                   Nodecontents  
                   Tiefe 4        \ 
    Nodename                        Nodename 
    Nodecontents                    Nodecontents
    Tiefe 5      \                / Tiefe 3      \
                   NodeName         
                   Nodecontents  
                   Tiefe 4         
    
    .......
    

    Aus dieser Darstellung im File kann man dann jede beliebige Drucker/Graphik/Bilschirm erzeugen

    Viel Spaß 😃 😃



  • Danke erstmal bis hier!

    @PAD: wie todo schon vermutet hat brauch den *parent um den baum nach dem loeschen eines Knotens wieder richtig zu verknuepfen.

    Deine Loesung ist im Prinzip das was ich meine...nur...und da steh ich immer noch aufm schlauch...mir is das prinzip klar nur will mir nicht die passende syntax dazu kommen 😕

    Ich poste vielleicht mal meinen kompletten source:

    // Includes 
    #include <stdio.h>  
    #include <stdlib.h> 
    
    // Einfacher eigener Datentyp node
    typedef struct node {
      char *name;
      struct node *parent;
      struct node *left;
      struct node *right;
    } node;
    
    // Funktionsdefinitionen
    node *create_node(void);
    int insert_node(char *s);
    int delete_node(char *s);
    int show_tree(node *knoten);
    
    node *anfang;
    
    int main(void)
    {
            //Auswahl  
    	int choice=99;
    	//Name des zu erstellenden Knotens
    	char newname[10];
    	//Name des zu loeschenden Knotens
    	char delname[10];
    
    	printf("BinaerBaum mit Loeschfunktion\n\n\n");
    
    	//...letzendlich die Abbruchbedingung fuer das ganze Programm
    	while (choice!=0)
    	{
    
    	printf("Knoten erstellen (1),loeschen (2),ausgabe (3) oder exit (0): ");
    	scanf("%d",&choice);
    	//Menuefuehrung
    	switch(choice)
    		{
    
    	case 1:
    
    	printf("\nBitte Bezeichnung des Knotens waehlen\n");
    	//Eingabe des Bezeichners fuer den neuen Knoten
    	scanf("%s",newname);
    	//Erstellt den neuen Knoten
    	insert_node(newname);
    	printf("\nKnoten %s erstellt\n",newname);
    		break;
    
    	case 2:
    	printf("\nBitte die EXAKTE(!!!) Bezeichnung des zu loeschenden Knotens eingeben\n");
    	//Dasselbe in Gruen...
    	scanf("%s",delname);
    	delete_node(delname);
    	printf("\nKnoten %s geloescht\n",delname);
    		break;
    
    	case 3:
    		printf("\n Baum wird ausgegeben:\n\n");
    		// Gib den Baum aus
    		 show_tree(anfang);
    			break;
    
    		}
    	printf("\n\n\n\n");
    	}
    
    printf("Programmende\n\n");
      return 0;
    }
    
    // Erstelle einen neuen Knoten (Speicher reservieren + Anfangswerte setzen)
    node *create_node(void)
    {
      // Reserviere Speicher fuer einen neuen Knoten
      node *knoten = malloc(sizeof(struct node));
    
      // Startwerte setzen
      knoten->name = NULL;
      knoten->parent = NULL;
      knoten->left = NULL;
      knoten->right = NULL;
      return knoten;
    }
    
    // Fuege einen neuen Knoten in den Baum ein
    int insert_node(char *s)
    {
      node *knoten, *current, *last;
      int richtung = 0;
      int vergleich = 0;
    
      // Neuen Knoten erstellen
      if( (knoten = create_node()) == NULL) 
        { 
          printf("Duh! We ran out of memory! :-(\n");
          exit(1);
        }
    
      // Speicher fuer den String allozieren
      knoten->name = malloc(sizeof(*s));
      strcpy(knoten->name,s);
    
      // Hat der Baum schon eine Wurzel?
      if(anfang == NULL)
        {
          anfang = knoten;
        }
      else
        {
          // Hangel Dich vom Anfang durch den Baum...
          current = anfang;
    
          // ...und suche die passende Speicherstelle
          while(current != NULL)
    	{
    	  // Merke Dir den letzten Ast
    	  last = current;
    
    	  // Vergleiche die Strings
    	  vergleich = strcmp(knoten->name,current->name);
    
    	  // Wenn der einzufuegende String "kleiner" ist,
    	  // dann gehts links lang
    	  if(vergleich < 0)
    	    {
    	      richtung = 0;
    	      current = current->left;
    	    }
    
    	  // Wenn er groesser ist, gehts rechts lang
    	  else if(vergleich > 0)
    	    {
    	      richtung = 1;
    	      current = current->right;
    	    }
    
    	  // Diesen String gibt es schon im Baum
    	  else
    	    {
    	      printf("insert_node(): String %s exists!\n",s);
    	      return 1;
    	    }
    	}
    
          // Vater des einzufuegenden Knotens setzen
          knoten->parent = last;
    
          // Ist der neue Knoten links oder rechts vom Vater?
          if(richtung == 0)
    	{
    	  last->left = knoten;
    	}
          else
    	{
    	  last->right = knoten;
    	}
        }
    
      return 0;
    }
    
    // Loesche einen Knoten aus dem Baum
    int delete_node(char *s)
    {
      node *current, *parent, *new;
      int vergleich = 0;
      int richtung = 0;
    
      // Suche den zu loeschenden Knoten
      current = anfang;
    
      while(current != NULL)
        {
          // Vergleiche die Strings
          vergleich = strcmp(s,current->name);
    
          // Wenn der String "kleiner" ist, bieg links ab
          if(vergleich < 0)
    	{
    	  richtung = 0;
    	  current = current->left;
    	}
    
          // Ist der String "groesser", bieg rechts ab
          else if(vergleich > 0)
    	{  
    	  richtung = 1;
    	  current = current->right;
    	}
    
          // Wir haben den passenden Knoten gefunden
          else
    	{
    	  break;
    	}
        }
    
      // Keinen passenden Eintrag gefunden?
        if(current == NULL)
        {
          printf("delete_node(): Cant find node %s!\n",s);
          return 1;
        }
    
      // Loeschen wir gerade das Wurzel Element?
      if(current == anfang)
        {
          // Ist das Wurzel Element das letzte Element des Baums?
          if( (current->left == NULL) && (current->right == NULL) )
    	{
    	  anfang = NULL;
    	}
          else
    	{
    	  // Gibt es einen linken Knoten?
    	  if(current->left != NULL)
    	    {
    	      anfang = current->left;
    	    }
    
    	  // Ansonsten wird der rechte genommen
    	  else
    	    {
    	      anfang = current->right;
    	    }
    	}
        }
    
      // Es wird nicht das Wurzel Element geloescht
      else
        {
          // Hat der zu loeschende Knoten noch Kindknoten?
          if( (current->left != NULL) || (current->right != NULL) )
    	{
    	  // Falls es einen linken Knoten gibt, nimmt dieser
    	  // den Platz des zu loeschenden ein
    	  if(current->left != NULL)
    	    {
    	      new = current->left;
    	    }
    
    	  // Ansonsten wird der rechte genommen
    	  else
    	    {
    	      new = current->right;
    	    }
    	}
          else
    	{
    	  new = NULL;
    	}
    
          // An welchem Ast des Vaters haengt der zu loeschende Knoten?
          parent = current->parent;
    
          if(richtung == 0)
    	{
    	  parent->left = new;
    	}
          else
    	{
    	  parent->right = new;
    	}
        }
    
      // Speicher des Knotens deallozieren
      free(current);
    
      return 0;
    }
    
    int show_tree(node *knoten)
    {
      if(knoten != NULL)
        {
    
          // Funktion rekursiv fuer den linken und rechten Ast aufrufen
    
          show_tree(knoten->left);
    	  // String ausgeben
    	  if (knoten->parent != 0)
    	// printf("%s hat den Elternknoten %s\n",knoten->name,knoten->parent->name);
    	printf("%s\n",knoten->name);
    
          show_tree(knoten->right);
        }
    
      return 0;
    }
    

    sry wenn ich den eindruck erwecke das man mir es vorkauen muss...mit 4 stunden mehr schlaf faellts mir auch mit sicherheit ein... 😞



  • dann hol dir den Schlaf

    Wenn man einen Knoten löschen möchte mache ich dies immer an der Stelle wo der Parent steht, kann mit diesen dann lokal in einer Temporären Variablen merken, und muß nicht einen Pointer mitschleifen und sicherstellen, das er Richtig ist.

    Was meinst du mit passender Syntax.

    Das durchlaufen eines Binarbaums ist eine einfache Übung. Siehe Donald E Knuth.

    Man fängt das meistens damit an, das man von Root aus solange den linken Ast wählt bis man bei einem Blatt ist.
    danach geht man eins hoch prüft ob rechts eine Blatt ist falls jeht geht man dorthin und sucht wieder das äußerst linke Blatt in diesem Ast auf, falls nein geht man den Ast eins höher und fängt von vorne an.
    ..... Somit sieht man das ein rekursiver Prozess ist mit sinnvolen Wegebschreibungen und Abbruchbedingungen.

    Beim ersten durchlauf sollte man sich die Tiefe des Baums mit merken jede Ebene die man tiefer hineinsteigt sollte man sich merken ist dies tiefer als die bisherige tiefste merkt man sich diese. Sinn der sache damit weis ich beim zweiten Durchlauf des Baumes wievele Leerzeichen ich vor den aktuellen Block drucken muß damit der tiefste ganz links steht und meiner aktuelle Knoten an der Richtigen Stelle ist.

    Das zweite Durchlaufen (Traversieren??) sucht den äußerst linken Ast auf schreibt die bnötigte Anzahl von Leerzeichen (Maximaltiefe - aktueller Tiefe)* Anzahl_Zeichen_Pro_Block vor den Daten des aktuellen Blocks.
    somit hast du deine Ausgabe



  • PAD schrieb:

    Wenn man einen Knoten löschen möchte mache ich dies immer an der Stelle wo der Parent steht, kann mit diesen dann lokal in einer Temporären Variablen merken, und muß nicht einen Pointer mitschleifen und sicherstellen, das er Richtig ist.

    irgendwie hab ich den eindruck, dass du was gegen parent hast... 🙂 bist du so ein speichergeizkragen 😉 wenn die lösch-funktion als parameter nur einen ptr auf das zu löschende element bekommt, ist so ein parent-ptr ziemlich praktisch. ok, man kann natürlich, wenn der root-node global bekannt ist, vom root-node aus den node suchen, deren left oder right dem zu löschenden element entspricht, ist aber (vorallem bei größeren bäumen) ziemlich ineffizient, schon rein vom algo her....



  • 😃 Wenn man sinnvoll sparen kann, warum nicht 😃 . ich brauch doch nicht jedesmal von root aus suchen.
    in der rekursion 1 Ebene höher und schon ist der Fisch geputzt. da muß ich ja sowieso hin um die Verknüpfung nach unten wieder zu machen.

    Aber ich glaub das ist nicht das Primaerproblem von seraphin was hier behandelt wird. 🕶


Anmelden zum Antworten