Comment bien insérer/supprimer dans un arbre de recherche binaire en C?
J'ai un peu obligé de mettre mon précédent C questions sur la prise de provoquer ce que l'on est de plus en plus importants...
J'ai déjà codé de l'insérer et de supprimer des fonctions sur mon arbre de recherche binaire, mais la fonction de suppression est incomplète. Il ya un couple de choses que j'ai besoin d'aide dans...
1) Est mon insérer une fonction bien ou peut-il être amélioré en quelque sorte?
2) Ma fonction de suppression manque la suppression d'un nœud à la fois à gauche et à droite de childs. J'ai beaucoup cherché dans le passé quelques heures, mais ne pouvait pas trouver un bon moyen de le faire.
2.a) Comment dois-je supprimer un nœud avec 2 nœuds enfants?
2.b) Comme dans la première question, est-ce la fonction de suppression de bonne ou peut-il être amélioré? Celui-ci, je sais qu'il peut, parce que je répète beaucoup de code dans les fi mais je ne vois pas comment je peux l'améliorer, j'ai besoin d'aide sur ce que trop.
typedef struct sClientProfile *ClientProfile;
typedef struct sClientTree *ClientTree;
typedef struct sClientProfile {
char *clientName;
int clientAge;
int clientNIF;
} nClientProfile;
typedef struct sClientTree {
ClientProfile clientProfile;
char *clientName;
ClientTree leftTree;
ClientTree rightTree;
} nClientTree;
void addClientToTree(ClientTree *cTree, ClientProfile cProfile) {
if(!*cTree) {
ClientTree new = (ClientTree)malloc(sizeof(nClientTree));
if(!new) {
perror("malloc");
}
new->clientName = strdup(cProfile->clientName);
new->clientProfile = cProfile;
new->leftTree = NULL;
new->rightTree = NULL;
*cTree = new;
} else {
if(strcmp((*cTree)->clientName, cProfile->clientName) > 0) {
addClientToTree(&(*cTree)->leftTree, cProfile);
} else {
addClientToTree(&(*cTree)->rightTree, cProfile);
}
}
}
void deleteClientFromTree(ClientTree *cTree, char *cName) {
if(!cTree) return;
int nCompare = strcmp((*cTree)->clientName, cName);
if(nCompare > 0) {
deleteClientFromTree(&(*cTree)->leftTree, cName);
} else if(nCompare < 0) {
deleteClientFromTree(&(*cTree)->rightTree, cName);
} else {
if(!(*cTree)->leftTree && !(*cTree)->rightTree) {
ClientTree cliPtr = *cTree;
free(cliPtr->clientProfile);
free(cliPtr);
cliPtr->clientProfile = NULL;
cliPtr = NULL;
*cTree = NULL;
} else if(!(*cTree)->leftTree) {
ClientTree cliPtr = *cTree;
free(cliPtr->clientProfile);
free(cliPtr);
cliPtr->clientProfile = NULL;
*cTree = (*cTree)->rightTree;
} else if(!(*cTree)->rightTree) {
ClientTree cliPtr = *cTree;
free(cliPtr->clientProfile);
free(cliPtr);
cliPtr->clientProfile = NULL;
*cTree = (*cTree)->leftTree;
} else {
//MISSING DELETE CASE
}
}
}
Vous aurez probablement remarqué, mais permettez-moi juste de faire 2 remarques:
- Cet arbre utilise des chaînes à la place de l'int de la représentation. C'est pourquoi je utiliser strcmp() tout le chemin, je pense que je vais l'utiliser.
- Je ne suis pas en utilisant la récursivité, je préfère passer le pointeur (un pointeur de structure dans ce cas) et de travailler avec cela. Il a l'air plus propre en quelque sorte et dans l'avenir je veux retourner une valeur de succès si un nœud a été supprimé.
DE MISE À JOUR CI-DESSOUS:
J'ai déjà fait ma version itérative de la fonction supprimer mais je n'aime pas certaines choses à ce sujet, peut-être qu'ils peuvent être améliorés (ou pas) mais je ne vois pas comment. J'ai aussi essayé de code le cas, il a été absent, la suppression d'un nœud avec 2 enfants, mais il ne fonctionne pas comme il se doit...
J'ai commenté le code entier où je pense que le code peut être amélioré et où est le problème. J'ai également nommé ces problèmes, B (il n'y a pas de B plus), C et D afin que nous puissions référence à eux facilement.
bool deleteClientFromTree(ClientTree *cTree, char *cName) {
if(!cTree) return FALSE;
ClientTree currPtr = *cTree;
ClientTree prevPtr = NULL;
int nCompare;
while(currPtr) {
nCompare = strcmp(currPtr->clientName, cName);
if(nCompare > 0) {
prevPtr = currPtr;
currPtr = currPtr->leftTree;
} else if(nCompare < 0) {
prevPtr = currPtr;
currPtr = currPtr->rightTree;
} else {
/*
* A)
*
* The following cases have 3 lines in common, the free()
* calls and return statement. Is there anyway to improve
* this code and make it more compact?
*
* Of course, the printf's are to be removed...
*/
if(!prevPtr && !currPtr->leftTree && !currPtr->rightTree) {
printf("CASE #1\n");
*cTree = NULL;
free(currPtr->clientProfile);
free(currPtr);
return TRUE;
} else if(!currPtr->leftTree || !currPtr->rightTree) {
printf("CASE #2\n");
if(prevPtr->leftTree == currPtr) {
prevPtr->leftTree = currPtr->rightTree;
} else {
prevPtr->rightTree = currPtr->leftTree;
}
free(currPtr->clientProfile);
free(currPtr);
return TRUE;
} else {
printf("CASE #3\n");
ClientTree tempPtr = currPtr->rightTree;
while(tempPtr->leftTree) {
tempPtr = tempPtr->leftTree;
}
/*
* C)
*
* This has a big problem...
*
* If you take a look at the ClientProfile structure,
* in the first post, you'll see two ints
* (clientNIF/clientAge) and one char* (clientName).
*
* The problem is that the following code line is only
* copying the integer data, not the string. For some
* reason, the string remains the old one.
*
* I tried to use strdup() directly on clientName like:
* currPtr->clientProfile->clientName = strdup(tempPtr->clientProfile->clientName);
* but it still doesn't work.
*
* Why everything is being copied but the strings?
*/
currPtr->clientProfile = tempPtr->clientProfile;
/*
* D)
*
* Is there anyway to not call the function itself
* and make the while loop once again and delete the
* corresponding leaf?
*/
return deleteClientFromTree(&currPtr->rightTree, tempPtr->clientProfile->clientName);
}
}
}
return FALSE;
}
J'aime un peu de typedef les pointeurs 🙂 Merci pour les autres conseils.
Cacher les pointeurs dans le typedef est moche.
Cacher les pointeurs dans un typedef pourrait être très problématique plus tard
OriginalL'auteur Ricardo Amaral | 2009-03-24
Vous devez vous connecter pour publier un commentaire.
Lorsque vous supprimer un nœud, vous devez faire quelque chose à propos de ses enfants.
Si il n'y a pas les enfants, pas de problème. Vous venez de supprimer le nœud.
Si il y a une gauche de l'enfant, aussi pas de problème; vous supprimer le nœud et le déplacer vers la gauche de l'enfant à sa place.
De même pour le droit de l'enfant; il suffit de déplacer l'enfant à l'endroit de l'élément supprimé.
Le problème vient lorsque vous souhaitez supprimer un nœud qui possède à la fois de droite et de gauche des enfants. Vous pouvez déplacer le gauche ou le droit de l'enfant à l'endroit de l'élément supprimé, mais que faites-vous ensuite sur les autres à l'enfant et à sa descendance?
Solution est cela; vous recherchez le successeur logique pour le nœud supprimé. Par le successeur logique, je veux dire; dire que vous avez un arbre fait de nombres entiers et vous supprimer le nœud avec la valeur 35, le successeur logique est le prochain plus grand nombre. Ya? si vous avez été faire un ordre de marche, il serait l'élément que vous venez à bout de l'élément que vous supprimez.
Maintenant, il y a une règle simple pour trouver le successeur logique; vous allez à droite (vous avez toujours le droit, parce que c'est le cas lorsque vous avez deux enfants) et puis vous allez à gauche aussi loin que vous le pouvez.
Cet élément en fin de compte vous est le successeur logique. Elle est plus grande que l'élément supprimé (vous êtes allé droit au début, vous vous souvenez?) mais c'est la plus petit côté plus grand élément.
Maintenant, cet élément a TOUJOURS un ou sans enfants - parce que vous êtes allé à gauche aussi loin que vous le pouvez, vous vous souvenez? donc vous ne pouvez pas aller à gauche toute plus - parce qu'il n'y est pas de gauche, de sorte que l'élément n'a pas d'enfants ou tout simplement un droit de l'enfant ce qui signifie qu'il tombe dans l'un de facile à dissocier les catégories (pas d'enfant ou un seul enfant). Afin de supprimer les liaisons ce élément est facile.
Maintenant vient la fraîcheur de bits considérons ceci: si le prochain plus grand élément ont été dans le même endroit dans l'arborescence de l'élément que vous souhaitez supprimer, l'arbre serait encore valide et correct - parce que tout à gauche de chaque élément est plus petit, tout à droite est plus grande.
Alors ce que vous faites; vous copiez les données de l'utilisateur dans le prochain plus grand nœud dans le nœud supprimé et que vous supprimez que le prochain plus grand nœud (il n'a pas d'enfants ou tout simplement un droit de l'enfant, de sorte qu'il est facile de les dissocier et de les supprimer).
Et c'est tout!
Donc, en gros - trouvez votre successeur logique, dissocier de lui de l'arbre et de mettre ses données de l'utilisateur dans l'élément que vous êtes en fait à l'origine de la suppression (pour ne pas puis de les supprimer, bien sûr, parce que c'est toujours physiquement partie de l'arbre).
OriginalL'auteur
Tout d'abord, vous avez mentionné que vous n'utilisez pas la récursivité, mais chaque fonction a un chemin logique qui s'appelle elle-même.
Sur les questions suivantes:
1)
Supprimer la récursivité. Cette pouvez-vous obtenir dans un tas d'ennuis si votre arbre est assez grand pour souffler votre pile. Gcc a un support limité pour les la queue la récursivité, mais je ne voudrais pas compter sur elle.
2)
Généralement, lorsque vous supprimez un enfant avec deux nœuds, vous promouvoir la gauche ou la droite nœud à la position de l'élément supprimé. (C'est un très simpliste cas, je suis en supposant que votre arbre n'est pas équilibré)
2.b)
Votre code de suppression a quelques problèmes. Je vous recommande la marche à travers elle avec un peu de situations hypothétiques. Immédiatement évident pour moi était libre avec un pointeur, puis deferencing:
Bien sûr, vous pouvez toujours vous soucier de style une fois que vous obtenez l'exactitude chose au carré.
Je viens de faire la fonction insérer itératif, je vais essayer de faire supprimer un est bien. À propos de la déférence, je préfère de cette façon, donc je n'ai pas "balançant des pointeurs". Il semblait la meilleure option en fonction de l'article de wikipédia sur le sujet.
Une balançant pointeur est exactement ce que vous avez quand vous gratuit cliPtr, cliPtr est maintenant un pointeur c'est pointant vers des données non valides. Tout comme l'article de wikipédia etats..
OriginalL'auteur matt_h
Idéalement, il y a trois cas de suppression d'un nœud dans le BST:
Cas 1:
Cas 2:
Cas 3:
Donc pour le manque de supprimer de cas:
Lorsque X (nœud à supprimer) a deux enfants, remplacez X par le successeur de X et de suivre le cas n ° 1 ou le cas n ° 2. Vous pouvez également remplacer son prédécesseur, peut être une bonne alternative.
if ( X->left && X->right)
{
}
OriginalL'auteur Ganesh M
ce codes binaires sont à insérer, supprimer,rechercher et arrêter de fumer.
Exemples:
OriginalL'auteur Greggy
OriginalL'auteur A.Achour