Passer au forum
Arbre binaire de re...
 
Notifications
Retirer tout

[Fermé] Arbre binaire de recherche en C (aide)

70 Messages
11 Utilisateurs
0 Réactions
5,758 Vues
chika78
Inscrit : 06.08.2009
PokerStrategist

C'est sûr, récursif c'est plus pur :

struct node* deleteKeyRecur(int i, struct node* r) {
  if (r != NULL) {
    if (i > r->value) {
      r->rChild = deleteKeyRecur(i, r->rChild);
    }
    else if (i < r->value) {
      r->lChild = deleteKeyRecur(i, r->lChild);
    }
    else {
      /* on a la cle cherchee */
      if (r->lChild == NULL && r->rChild == NULL) {
        r = NULL;
      }
      else if (r->lChild != NULL && r->rChild == NULL) {
        r = r->lChild;
      }
      else if (r->rChild != NULL && r->lChild == NULL) {
        r = r->rChild;
      }
      else {
        /* deux feuilles */
        if (r->rChild->lChild == NULL) {
          r->rChild->lChild = r->lChild;
          r = r->rChild;
        }
        else {
          struct node* q;
          struct node* p = r->rChild;
          while (p->lChild->lChild != NULL)
            p = p->lChild;
          q = p->lChild;
          p->lChild = q->rChild;
          q->lChild = r->lChild;
          q->rChild = r->rChild;
          r = q;
        }
      }
    }
  }
  return r;
}

du coup, dans le main(), il faut utiliser

  T->root = deleteKeyRecur(6, T->root);

Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Et du coup ce n'est plus la même fonction si il y a une node en argument... :P

Et ouais :P


chika78
Inscrit : 06.08.2009
PokerStrategist

Certes. Mais par principe, une fonction récursive doit agir sur des arguments de même type.

En gros, une fonction récursive avec un tree en param ne peut agir que sur un tree. Or, la structure du tree n'est pas elle-même "récursive" puisqu'elle ne contient qu'un node.

Donc, si tu veux faire la fonction deleteKey telle qu'elle t'est proposée, elle ne peut pas être récursive.


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Y a pas moyen de la faire récursive comme les autres avec un pointeur d'arbre qui parcoure l'arbre a chaque récursion (voir les codes précédents) ?

En gros T-> root change a chaque récursion grâce a ce dernier, la seule difficulté supllémentaire dans deleteKey c'est qu'il faut aussi se rappeller du noeud parent...


chika78
Inscrit : 06.08.2009
PokerStrategist

Si tu dois reparcourir l'arbre à chaque récursion, il n'y a plus d'intérêt à faire une fonction récursive.

Perso, j'irais voir le prof pour lui dire que utiliser sa struct tree pour son arbre, ben c'est idiot, il faut utiliser uniquement la struct node dans les fonctions d'arbre. :D

Attends de faire une liste doublement chaînée ou un quicksort, et tu comprendras vite.

Je vais illustrer mon propos. Imagine ces bouts de code :

struct valeur {
  unsigned long L;
}

unsigned long factValeur(struct valeur* L) {
}

unsigned long factorielle(unsigned long i) {
  return ( i == 1 ? i : i * factorielle(i-1) );
}

Et maintenant, vas-y, écris la fonction factValeur récursive...


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Oui sand doute mais on ne reparcoure pas l'arbre a chaque récursion, y a juste le T->root qui change de place a chaque récursion (généralement vers l'élément suivant), donc la complexité ne dépasse pas celle d'une fonction récursive node, ca reste O(n) dans la, plupart des cas.


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Bon vu que ta version marche super, tant que je ne trouve pas une manière récursive de gérer la chose je la garde (me reste plus qu'à bien la comprendre et là j'ai du mal :P

Me lance dans le point deux !

2 Arbre enrichi - 4 points Ajoutez deux champs complémentaires au type structuré correspondant aux noeuds de l’arbre : un champ size et un champ height, qui représenteront le nombre d’éléments dans le sous-arbre dont le noeud est la racine, et la hauteur du sous-arbre. Ces champs devront être mis à jour à chaque insertion et suppression. Adaptez le parcours "en-ordre" pour qu’il affiche, en plus de la valeur du noeud, les champs size et height.

Ca devrait pas être trop compliqué, non ?
Rien de bien différent a ce qu'on a fait jusqu'ici.


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

JivJav/Chika

Vous croyez que c'est toujours possible pour résoudre le point 2 de garder la même fonction que la 1 en ne modifiant que le code interne (en gros je garde les mêmes paramètres envoyés a la fonction et je ne rajoute pas de fonction a côté !!).

Parce que j'ai le même problème que rencontré avec pTreeparent pour résoudre récursivement deleteKey : comment sauvegarder un changement de variable a chaque "récursion" alors qu'on ne le passe pas en paramètre ?


chika78
Inscrit : 06.08.2009
PokerStrategist

C'est le problème en effet, sans lien vers le parent dans la feuille c'est difficile.

Regarde ma version, j'applique une autre technique :D

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> 


struct node {
  struct node *lChild;
  struct node *rChild;
  int value;
  int size;
  int height;
};

struct tree {
  struct node *root;
};



struct node* constructNewNode(int i) {
  /* cree une nouvelle instance de node */
  struct node* n = (struct node *)malloc(sizeof(struct node));
  n->value = i;
  n->lChild = n->rChild = NULL;
  return n;
}



struct node* nodeInsert(int i, struct node* n) {
  if (i < n->value) 
    /* si la feuille gauche est non nulle, on insere dessus, sinon on la cree */
    return ( n->lChild != NULL ? nodeInsert(i, n->lChild) : (n->lChild = constructNewNode(i)) );
  else 
    /* si la feuille droite est non nulle, on insere dessus, sinon on la cree */
    return ( n->rChild != NULL ? nodeInsert(i, n->rChild) : (n->rChild = constructNewNode(i)) );
}


int nodeCalcSize(struct node* n) {
  int s = 0;
  if (n != NULL) {
    if (n->lChild != NULL) s += (1+nodeCalcSize(n->lChild));
    if (n->rChild != NULL) s += (1+nodeCalcSize(n->rChild));
    n->size = s;
  }
  return s;
}


int nodeCalcHeight(struct node* n) {
  int h = 0;
  if (n != NULL) {
    int hl;
    int hr;
    if (n->lChild != NULL) hl = 1+nodeCalcHeight(n->lChild);
    if (n->rChild != NULL) hr = 1+nodeCalcHeight(n->rChild);
    h = max(hl, hr);
    n->height = h;
  }
  return h;
}


struct node* nodeFindKey(int i, struct node* n)
{
  if (n != NULL) {
    if (i == n->value)
      return n;
    else if (i < n->value)
      return nodeFindKey(i, n->lChild);
    else
      return nodeFindKey(i, n->rChild);
  }
  else 
    return NULL;
}


void nodeDeleteKey(struct node* n) {
  if (n != NULL) {
    nodeDeleteKey(n->lChild);
    nodeDeleteKey(n->rChild);
    free(n);
  }
}


struct tree* constructNewTree() {

  struct tree *T = malloc(sizeof(struct tree));
  T->root = NULL;

  return T;
}


void insertKey(int i, struct tree* T) {
  if (T->root == NULL) 
    T->root = constructNewNode(i);
  else
    nodeInsert(i, T->root);
  nodeCalcSize(T->root);
  nodeCalcHeight(T->root);
}


bool findKey(int i, struct tree *T) {
  return ( nodeFindKey(i, T->root) != NULL );
}


void deleteKey(int i, struct tree *T) {
  struct node* temp; 
  struct node* prev; 
  
  temp = prev = T->root;
  
  while (temp != NULL)  { 
    if (temp->value == i) {
      /* on a trouve la key a effacer */
      bool isLeft = (prev->lChild == temp);
      /* cas ou une seule feuille vide */
      if (temp->lChild == NULL) {
        if (isLeft) 
          prev->lChild = temp->rChild; 
        else
          prev->rChild = temp->rChild; 
      }
      else if (temp->rChild == NULL) {
        if (isLeft) 
          prev->lChild = temp->lChild; 
        else
          prev->rChild = temp->lChild; 
      }
      else {
        /* le cas ou les 2 feuilles ne sont pas vides */
        while (temp->lChild != NULL && temp->rChild != NULL) { 
          temp->value = temp->rChild->value; 
          prev = temp; 
          temp = temp->rChild; 
        } 
        
        if (temp->rChild == NULL) prev->rChild = temp->lChild; 
        if (temp->lChild == NULL) prev->rChild = temp->rChild; 
      }
      free(temp); 
      return; 
    } 
    
    prev = temp; 
    
    if (temp->value < i) 
      temp = temp->rChild; 
    else 
      temp = temp->lChild; 
  } 
  
  nodeCalcSize(T->root);
  nodeCalcHeight(T->root);

} 


struct node* deleteKeyRecur(int i, struct node* r) {
  if (r != NULL) {
    if (i > r->value) {
      r->rChild = deleteKeyRecur(i, r->rChild);
    }
    else if (i < r->value) {
      r->lChild = deleteKeyRecur(i, r->lChild);
    }
    else {
      /* on a la cle cherchee */
      if (r->lChild == NULL && r->rChild == NULL) {
        r = NULL;
      }
      else if (r->lChild != NULL && r->rChild == NULL) {
        r = r->lChild;
      }
      else if (r->rChild != NULL && r->lChild == NULL) {
        r = r->rChild;
      }
      else {
        /* deux feuilles */
        if (r->rChild->lChild == NULL) {
          r->rChild->lChild = r->lChild;
          r = r->rChild;
        }
        else {
          struct node* q;
          struct node* p = r->rChild;
          while (p->lChild->lChild != NULL)
            p = p->lChild;
          q = p->lChild;
          p->lChild = q->rChild;
          q->lChild = r->lChild;
          q->rChild = r->rChild;
          r = q;
        }
      }
    }
  }
  return r;
}



void nodeInOrder(struct node* n) {
  if (n != NULL) {
    nodeInOrder(n->lChild);
    printf("V:%d, S:%d, H:%d \n", n->value, n->size, n->height);
    nodeInOrder(n->rChild);
  }
}


void inOrder(struct tree *T) {
  nodeInOrder(T->root);
  printf("\n");
}



void deleteTree(struct tree *T) {
  nodeDeleteKey(T->root);
  free(T);
  T = NULL;
}


struct valeur {
  unsigned long L;
}

unsigned long factValeur(struct valeur* L) {
}

unsigned long factorielle(unsigned long i) {
  return ( i == 1 ? i : i * factorielle(i-1) );
}


int main() {
  struct tree* T;
  
  printf("Debut\n");
  printf("Fact(8) = %li \n", factorielle(8));
  

  T = constructNewTree();

/*
  insertKey(5, T);
  insertKey(2, T);
  insertKey(9, T);
  insertKey(1, T);
*/

  insertKey(10, T);
  insertKey(7, T);
  insertKey(8, T);
  insertKey(9, T);
  insertKey(5, T);
  insertKey(4, T);
  insertKey(6, T);
  insertKey(14, T);
  insertKey(11, T);
  insertKey(18, T);

  inOrder(T);

  T->root = deleteKeyRecur(6, T->root);
  inOrder(T);

  deleteTree(T);
  
  printf("Fin \n\n");
  return 0;
}

jivjav
Inscrit : 20.02.2007
Elite Grinder

Comme chika, sans lien vers le parent, c'est compliqué. Il faut passer par de nouvelles fonctions pour récupérer width/height.

J'ai l'impression que tu t'appliques comme principe de ne pas ajouter de nouvelles fonctions au code alors que de mon point de vue, c'est un leak que d'essayer de vouloir tout condenser. Il faut au contraire regrouper une partie de code que l'on pourrait qualifier d'atomique au sein d'une fonction/méthode, quitte à avoir "beaucoup" de fonctions


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Beh je crois/croyais surtout que la volonté du prof c'était a la base que tu te contentes des fonctions proposées et que tu les implémentes au mieux sans en rajouter, mais apparement c'est quasi impossible, surtout qu'il exige la récursivité quasi tout le temps :tongue:

Bon bon bon et de fait j'arrive vraiment pas a m'en sortir sans rajouter de fonction avec width et height ?(


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Yop Chika tu peux m'expliquer le fonctionnement précis de ta fonction deleteKey non récursive , j'ai un peu du mal a l'expliquer, à dire pourquoi tu utilises ca, pourquoi tu le fais comme ca (enfin les tu sont censés être des je :P )

Je n'ai pas pu garder ta si belle fonction récursive qui renvoyait une node parce qu'elle n'utilisait pas celle qui demandait l'arbre en paramètre et qui est dans le code de base donné par le prof . (et le fait qu'elle renvoit une node est un peu chiant de surcroit)

La non récursive pour rappel c'est celle là :

void deleteKey(int i, struct tree *T) {
struct node* tempNode;
struct node* prevNode;

tempNode = prevNode = T->root;

while (tempNode != NULL) {
if (tempNode->value == i) {
bool isLeft = (prevNode->lChild == tempNode);
if (tempNode->lChild == NULL) {
if (isLeft)
prevNode->lChild = tempNode->rChild;
else
prevNode->rChild = tempNode->rChild;
}
else if (tempNode->rChild == NULL) {
if (isLeft)
prevNode->lChild = tempNode->lChild;
else
prevNode->rChild = tempNode->lChild;
}
else {
while (tempNode->lChild != NULL && tempNode->rChild != NULL) {
tempNode->value = tempNode->rChild->value;
prevNode = tempNode;
tempNode = tempNode->rChild;
}

if (tempNode->rChild == NULL) prevNode->rChild = tempNode->lChild;
if (tempNode->lChild == NULL) prevNode->rChild = tempNode->rChild;
}
free(tempNode);
return;
}

prevNode = tempNode;

if (tempNode->value < i)
tempNode = tempNode->rChild;
else
tempNode = tempNode->lChild;
}

nodeCalcSize(T->root);
nodeCalcHeight(T->root);

}

Sinon b izarrement ca compile et tout mais il reste encore quelques problèmes puisque :

1) on ne devrait pas pouvoir rajouter deux fois le même élement (==> EDIT : CHECK)
2) problème avec size et height (les chiffres ne sont pas justes) (==>EDIT : CHECK POUR SIZE mais pas pour HEIGHT)

En gros ca donne çà :
affichage


chika78
Inscrit : 06.08.2009
PokerStrategist

Pb1 : le fait de ne pas pouvoir ajouter 2 fois le même élément est facile à implémenter :

struct node* nodeInsert(int i, struct node* n) {
  if (i < n->value) 
    /* si la feuille gauche est non nulle, on insere dessus, sinon on la cree */
    return ( n->lChild != NULL ? nodeInsert(i, n->lChild) : (n->lChild = constructNewNode(i)) );
  else if (i > n->value) 
    /* si la feuille droite est non nulle, on insere dessus, sinon on la cree */
    return ( n->rChild != NULL ? nodeInsert(i, n->rChild) : (n->rChild = constructNewNode(i)) );
  else 
    return (NULL);
}

pb2 : les size on l'air ok. Les height ont peut-etre un souci avec la fonction max() dans nodeCalcHeight().
Essaye de remplacer

h = max(hl, hr);

par

h = (hl > hr ? hl : hr);

Sinon, la fonction avec quelques commentaires en +. Le mieux est de visualiser sur papier les valeurs prises au cours du traitement, avec un arbre de test (gaffe à l'ordre d'insertion des valeurs)

void deleteKey(int i, struct tree *T) {
  struct node* temp; 
  struct node* prev; 
  
  temp = prev = T->root;
  
  /* premiere boucle while : on cherche la valeur à effacer dans l'arbre */
  while (temp != NULL)  { 
    /* regarde si on a trouve la key a effacer */
    if (temp->value == i) {
	  /* gotcha! */
      bool isLeft = (prev->lChild == temp);
	  /* isLeft contient TRUE si on est la feuille de gauche, sinon FALSE */

      /* On traite d'abord les cas ou le node a effacer a une seule feuille */
      if (temp->lChild == NULL) {
        if (isLeft) 
          prev->lChild = temp->rChild; 
        else
          prev->rChild = temp->rChild; 
      }
      else if (temp->rChild == NULL) {
        if (isLeft) 
          prev->lChild = temp->lChild; 
        else
          prev->rChild = temp->lChild; 
      }

	  /* ici on traite le cas ou la feuille a effacer a deux feuilles */
      else {
		/* declare autre pointeur pour ne pas confondre avec prev */
		struct node* last = prev;	
		/* on parcours les feuilles de droite, en remontant les valeurs au node du dessus */
        while (temp->lChild != NULL && temp->rChild != NULL) { 
          temp->value = temp->rChild->value; 
          last = temp; 
          temp = temp->rChild; 
        } 
        /* on replace les liens dans le node de la valeur effacee */
        if (temp->rChild == NULL) last->rChild = temp->lChild; 
        if (temp->lChild == NULL) last->rChild = temp->rChild; 
      }

      free(temp); /* libere memoire du node inutile */
      return; 
    } 
    
	/* on conserve le dernier node (le pere) */
    prev = temp; 
    
    if (temp->value < i) 
      temp = temp->rChild; 
    else 
      temp = temp->lChild; 
  } 
  
  nodeCalcSize(T->root);
  nodeCalcHeight(T->root);

} 
/*

        20
      /    \
    15      \
   /  \      30
  13   \    /  \ 
 /  \   \  25   35
11  14  18
       /  \
     16    19

*/

Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

1, déjà fait, 2 déjà fait marche pas (d'ailleurs on dirait vu les chiffres qu'elle renvoit l'adresse des height et pas leur valeur (non ?) )

3 je lis !

Merci encore une fois


chika78
Inscrit : 06.08.2009
PokerStrategist

Arf, ca doit venir du compilo, init à 0 des variables pas faite.

int nodeCalcHeight(struct node* n) {
  int h = 0;
  if (n != NULL) {
    /* init variables !! */
    int hl=0;
    int hr=0;
    if (n->lChild != NULL) hl = 1+nodeCalcHeight(n->lChild);
    if (n->rChild != NULL) hr = 1+nodeCalcHeight(n->rChild);
    h = (hl > hr ? hl : hr);
    n->height = h;
  }
  return h;
}

Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Bon ca ca marche, y a juste des petites erreurs parfois il rajoute un en trop pour la hauteur et la taille, surtout vers les nœuds du début... Bon je vais pas me prendre la tête avec ca c'est pas pour le pentagone...

3 Fusion - 4 points
Etant donnés deux arbres T et U, implémentez une opération de fusion des deux arbres,
qui consiste à mettre tous les éléments de U dans T. A l’issue de cette opération, l’arbre U
doit être vide. N’oubliez pas de mentionner la complexité de cette opération.

Me reste plus que ca, je devrais y arriver tout seul mais on sait jamais

Par contre j'ai toujours du mal a saisir toutes les lignes de ta fonction, va falloir que je relise en détail (surtout la partie avec deux feuilles)


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Dernier point : CHECK

Plus qu'à comprendre la fonction deleteKey et j'ai fini toute la partie code a peu de chose près , maintenant va falloir pondre un rapport :tongue:


chika78
Inscrit : 06.08.2009
PokerStrategist

T'as trouvé le bug dans deleteKey() ? :D

Vieux principe utile qui n'est pas appliqué : un seul point de sortie d'une fonction, c'est mieux.


Tchenou Auteur du sujet
Tchenou
Inscrit : 10.05.2008
Elite Grinder

Je t'ai dit je capte qu'à moitié deletekey donc non à mon avis je l'ai pas trouvé :D


chika78
Inscrit : 06.08.2009
PokerStrategist

Version a jour :

void deleteKey(int i, struct tree *T) {
  struct node* temp;
  struct node* prev;

  prev = NULL;
  temp = T->root;

  while (temp != NULL)  {
    if (temp->value == i) {
      /* on a trouve la key a effacer */
      if (prev != NULL) {
        bool isLeft = (prev->lChild == temp);
        /* cas ou une seule feuille vide */
        if (temp->lChild == NULL) {
          if (isLeft)
            prev->lChild = temp->rChild;
          else
            prev->rChild = temp->rChild;
        }
        else if (temp->rChild == NULL) {
          if (isLeft)
            prev->lChild = temp->lChild;
          else
            prev->rChild = temp->lChild;
        }
        else {
          /* le cas ou les 2 feuilles ne sont pas vides */
          while (temp->lChild != NULL && temp->rChild != NULL) {
            temp->value = temp->rChild->value;
            prev = temp;
            temp = temp->rChild;
          }

          if (temp->rChild == NULL) prev->rChild = temp->lChild;
          if (temp->lChild == NULL) prev->rChild = temp->rChild;
        }
      }
      free(temp);
      temp = NULL;
    }
    else {

      prev = temp;

      if (temp->value < i)
        temp = temp->rChild;
      else
        temp = temp->lChild;
    }
  }

  nodeCalcSize(T->root);
  nodeCalcHeight(T->root);

}