Inserimento in Albero Rosso/Nero [C]

di il
10 risposte

Inserimento in Albero Rosso/Nero [C]

Ciao a tutti, sono nuovo!
Sto impazzendo per implementare un semplice inserimento in un albero rosso nero in c ( ). La struct l'ho dichiarata in questo modo:
typedef enum {rosso, nero} colore_t;

typedef struct nodo
{
  int valore;
  colore_t colore;
  struct nodo *sinistro, *destro, *padre;
} alberorn_t;
Inserimento:
int inserisci(alberorn_t* sent, int valore)
{
  int inserito;
  alberorn_t *nodo, *padre, *nuovo;
  for(nodo = sent->sinistro, padre = sent;
      ((nodo != sent) && (nodo->valore != valore));
      padre = nodo, nodo = (valore < nodo->valore)?
                             nodo->sinistro:
                             nodo->destro);

  if(nodo != sent)
  {
     inserito = 0;
  }
  else
  {
     inserito = 1;
     nuovo = (alberorn_t *)malloc(sizeof(alberorn_t));
     nuovo->valore = valore;
     nuovo->colore = rosso;
     nuovo->sinistro = nuovo->destro = sent;
     nuovo->padre = padre;

     if(padre == sent)
     {
        sent->sinistro = sent->destro = nuovo;
     }
     else
     {
        if(valore < padre->valore)
        {
           padre->sinistro = nuovo;
        }
        else
        {
           padre->destro = nuovo;
        }
     }
     ripristina(sent, nuovo);
  }
  return(inserito);
}
Rotazione sinistra:
void rotazione_sx(alberorn_t* nodo, alberorn_t* x)
{
  alberorn_t *y;
  y = x->destro;
  x->destro = y->sinistro;
  y->sinistro->padre = x;
  y->padre = x->padre;

  if(x == nodo->sinistro)
  {
     nodo->sinistro = nodo->destro = y;
  }
  else 
  {
     if(x == x->padre->sinistro)
     {
        x->padre->sinistro = y;
     }
     else
     {
        x->padre->destro = y;
     }
  }
  y->sinistro = x;
  x->padre = y;
}
La rotazione destra è speculare alla sinistra.
Funzione di ripristino dopo un inserimento:
void ripristina(alberorn_t* sent, alberorn_t* nodo)
{
  alberorn_t *zio;
  
  while(nodo->padre->colore == rosso)
  {
     if(nodo->padre == nodo->padre->padre->sinistro)
     {
        zio = nodo->padre->padre->destro;
        if(zio->colore == rosso)
        {
           nodo->padre->colore = nero;
           zio->colore = nero;
           nodo->padre->padre->colore = rosso;
           nodo = nodo->padre->padre;           
        }
        else
        {
           if(nodo == nodo->padre->destro)
           {
              nodo = nodo->padre;
              rotazione_sx(sent, nodo);
           }
           nodo->padre->colore = nero;
           nodo->padre->padre->colore = rosso;
           rotazione_dx(sent, nodo->padre->padre);           
        }
     }
     else
     {
        zio = nodo->padre->padre->sinistro;
        if(zio->colore == rosso)
        {
           nodo->padre->colore = nero;
           zio->colore = nero;
           nodo->padre->padre->colore = rosso;
           nodo = nodo->padre->padre;           
        }
        else
        {
           if(nodo == nodo->padre->sinistro)
           {
              nodo = nodo->padre;
              rotazione_dx(sent, nodo);
           }
           nodo->padre->colore = nero;
           nodo->padre->padre->colore = rosso;
           rotazione_sx(sent, nodo->padre->padre);           
        }
     }
  }
  sent->sinistro->colore = nero;  
}
Per quanto riguarda il main:
int scelta;
  alberorn_t *sent;
  FILE *file;
  int valore;
  int esito;
  int n = 0;

  sent = (alberorn_t *)malloc(sizeof(alberorn_t));
  sent->sinistro = sent;
  sent->destro = sent;
  sent->padre = sent;
 
  while((scelta = menu()))
  {
     switch(scelta)
     {
     case 1:
        file = fopen("file.txt","r");
    
        while((fscanf(file, "%d", &valore)) == 1)
        {
          
           esito = inserisci(sent, valore);
           if(esito == 1)
           {
              printf("%d: Inserito\n", sent->valore);
           }
           else
           {
              printf("%d: Errore durante l'inserimento\n", sent->valore);
           }
           n++;
           printf("numero iterazioni: %d\n", n);
        }
        break;
Premetto che il codice è quello delle dispense del mio professore dell'università. A quanto pare non mi inserisce proprio nulla; se riuscite a dirmi dove sbaglio vi sarò eternamente grato
EDIT: Aggiungo che durante la compilazione non mi da nessun errore né warning, ma quando cerco di stampare l'albero con una semplice funzione di stampa mi dà il seguente errore:
"Errore di segmentazione (core dump creato)"

10 Risposte

  • Re: Inserimento in Albero Rosso/Nero [C]

    Anche la funzione di stampa è presa dalle dispense?
  • Re: Inserimento in Albero Rosso/Nero [C]

    oregon ha scritto:


    Anche la funzione di stampa è presa dalle dispense?
    No quella no, è una semplice funzione ricorsiva che va a stampare la parte sinistra, poi la radice e poi la parte destra. Questa per intenderci:
    void stampa_albero(alberorn_t* radice)
    {
      stampa_albero(radice->sinistro);
      printf("%d ", radice->valore);
      stampa_albero(radice->destro);
    }
    La cosa strana è che alla fine del ciclo il valore contenuto nella radice continua a essere 0 (valore che poi penso inizializzi da solo dato che non è presente uno 0 nel file.txt).

    EDIT: aggiungo per completezza il file.txt:
    7
    8
    97
    23
    4
    2
    5
  • Re: Inserimento in Albero Rosso/Nero [C]

    Sicuro che la funzione di stampa sia corretta?
  • Re: Inserimento in Albero Rosso/Nero [C]

    oregon ha scritto:


    Sicuro che la funzione di stampa sia corretta?
    Per i normali alberi binari ha sempre funzionato e in teoria dovrebbe andare anche qui, il rosso-nero rimane comunque un albero binario.
    Se invece è una domanda retorica perchè vedi un errore allora dammi un indizio perchè non ci arrivo
  • Re: Inserimento in Albero Rosso/Nero [C]

    Quando termina di chiamare se stesso? Segui la prima chiamata (la prima linea ...)
  • Re: Inserimento in Albero Rosso/Nero [C]

    oregon ha scritto:


    Quando termina di chiamare se stesso? Segui la prima chiamata (la prima linea ...)
    Ok credo di aver capito l'errore: non essendoci nessun puntatore a NULL come capita con i normali alberi binari la funzione non terminerà mai giusto?
    Quindi devo implementare un corretto algoritmo di visita per alberi rosso neri, il problema (che prima non mi sono posto) è come scrivo la clausola di terminazione del ciclo? Non essendoci nessun puntatore a NULL cosa sfrutto?

    Questa struttura dati mi sta uccidendo
  • Re: Inserimento in Albero Rosso/Nero [C]

    Perché non ci sono puntatori a NULL? E le foglie a cosa puntano?
  • Re: Inserimento in Albero Rosso/Nero [C]

    oregon ha scritto:


    Perché non ci sono puntatori a NULL? E le foglie a cosa puntano?
    Ho provato a modificare la funzione con un if:
    void stampa_in_ordine(alberorn_t* albero)
    {
       if(albero != NULL)
       {
          stampa_in_ordine(albero->sinistro);
          printf("%d\n", albero->valore);
          stampa_in_ordine(albero->destro);
       }
    }
    
    Ma il risultato è lo stesso. Non capisco proprio perchè la prima chiamata di funzione dia problemi dato che nei normali alberi binari mi ha sempre funzionato .

    EDIT: Credo di aver fatto una grossa confusione, io come parametro effettivo della funzione inserisco il nodo sentinella e non la radice dell'albero, può essere questo l'errore?
  • Re: Inserimento in Albero Rosso/Nero [C]

    Scusate il doppio post. Ho risolto modificando leggermente l'algoritmo di visita degli alberi binari:
    void stampa_in_ordine(alberorn_t* sent, alberorn_t* nodo)
    {
       if(nodo != sent)
       {
          stampa_in_ordine(sent, nodo->sinistro);
          printf("%d\n", nodo->valore);
          stampa_in_ordine(sent, nodo->destro);
       }
    
    
    Ora ho notato un problema: non inserisce i valori uguali presenti nel file txt (ad esempio se nel file ci sono due 7, il secondo 7 non lo inserisce).
    Ho provato a modificare un pezzo di codice della funzione di inserimento cambiando da < a <=, ma non va lo stesso.
    Prima:
    for(nodo = sent->sinistro, padre = sent;
          ((nodo != sent) && (nodo->valore != valore));
          padre = nodo, nodo = (valore < nodo->valore)?
                                 nodo->sinistro:
                                 nodo->destro);
    
    Dopo:
    for(nodo = sent->sinistro, padre = sent;
          ((nodo != sent) && (nodo->valore != valore));
          padre = nodo, nodo = (valore <= nodo->valore)?
                                 nodo->sinistro:
                                 nodo->destro);
    
    Quale può essere il problema?

    EDIT: Ho risolto, come un idiota non mi sono accorto della clausola terminante del for. Scrivendo il for in questo modo:
    for(nodo = sent->sinistro, padre = sent;
          ((nodo != sent)); padre = nodo, nodo = (valore <= nodo->valore)?
                                 nodo->sinistro:
                                 nodo->destro);
    
    Mi prende anche i valori identici. Spero che togliendo la clausola precedente non incorro in altri errori
  • Re: Inserimento in Albero Rosso/Nero [C]

    Riuppo il topic per cercare un chiarimento: continuando gli esercizi con l'albero rosso nero, mi viene chiesto di ordinare l'albero secondo un'altra chiave scelta dall'utente. Il mio ragionamento è stato quello di creare una funzione che sviluppa un albero di appoggio con l'ordinamento richiesto dall'utente.
    L'inserimento nel nuovo albero lo faccio attraverso una visita in ordine anticipato, quando poi vado a stampare il nuovo albero però c'è qualche nodo mancante e non capisco perché.
    Questa è la funzione che crea il nuovo albero:
    void costruisci_albero_accelerazione(alberorn_t *sent, alberorn_t *nodo, alberorn_t *sent_acc)
    {
      alberorn_t *nodo_acc,
                 *padre_acc,
                 *nuovo_acc;
      if (nodo == sent)
      {
         return;
      }
      for(nodo_acc = sent_acc->sinistro, padre_acc = sent_acc;
          ((nodo_acc != sent_acc));
          padre_acc = nodo_acc, nodo_acc = (nodo->elemento.accelerazione < nodo_acc->elemento.accelerazione)?
                                 nodo_acc->sinistro:
                                 nodo_acc->destro);     
      nuovo_acc = (alberorn_t *)malloc(sizeof(alberorn_t));
      nuovo_acc->elemento.tempo = nodo->elemento.tempo;
      nuovo_acc->elemento.velocita = nodo->elemento.velocita;
      nuovo_acc->elemento.accelerazione = nodo->elemento.accelerazione;
      nuovo_acc->elemento.giri_motore = nodo->elemento.giri_motore;
      nuovo_acc->elemento.angolo_sterzata = nodo->elemento.angolo_sterzata;
      nuovo_acc->colore = rosso;
      nuovo_acc->sinistro = nuovo_acc->destro = sent_acc;
      nuovo_acc->padre = padre_acc;     
    
      if(padre_acc == sent_acc)
      {
         sent_acc->sinistro = sent_acc->destro = nuovo_acc;
      }
      else
      {
         if(nodo->elemento.accelerazione < padre_acc->elemento.accelerazione)
         {
            padre_acc->sinistro = nuovo_acc;
         }
         else
         {
            padre_acc->destro = nuovo_acc;
         }
      }
      ripristina(sent_acc, nuovo_acc);
      if(nodo->sinistro != sent)
      {
         costruisci_albero_accelerazione(sent, nodo->sinistro, sent_acc);
      }
      if(nodo->destro != sent)
      {
          costruisci_albero_accelerazione(sent, nodo->destro, sent_acc);
      }   
    }
    
    Qui invece chiamo la funzione che va a modificare il file di input:
     scrivi_file(sent_acc, sent_acc->destro, file);
    E qui c'è la sua definizione:
    void scrivi_file(alberorn_t *sent, alberorn_t *nodo, FILE *file)
    {
      if(nodo != sent)
      {
         scrivi_file(sent, nodo->sinistro, file);
         fprintf(file, "%d\t\t%4.2f\t\t%4.2f\t\t\t%d\t\t\t%4.2f\n", nodo->elemento.tempo,
                                                                                       nodo->elemento.velocita,
                                                                                       nodo->elemento.accelerazione,
                                                                                       nodo->elemento.giri_motore,
                                                                                       nodo->elemento.angolo_sterzata);
         scrivi_file(sent, nodo->destro, file);
      }
    
    Il risultato è che a seconda dei dati di ingresso delle volte il file di output è giusto delle volte invece mancano proprio dei nodi, anche se attraverso delle printf ho visto che vengono inseriti quindi non capisco perchè non vengano stampati. Grazie a tutti quelli che mi daranno una mano
Devi accedere o registrarti per scrivere nel forum
10 risposte