Algorithme pour tester si un arbre binaire est un arbre de recherche et compter les branches complètes

10

Je dois créer un algorithme récursif pour voir si un arbre binaire est un arbre de recherche binaire ainsi que compter le nombre de branches complètes (un nœud parent avec les nœuds enfants gauche et droit) avec une variable de comptage globale supposée. Il s'agit d'une affectation pour ma classe de structures de données.

Jusqu'à présent, j'ai

void BST(tree T) {
   if (T == null) return
   if ( T.left and T.right) {
      if (T.left.data < T.data or T.right.data > T.data) {
        count = count + 1
        BST(T.left)
        BST(T.right)
      }
   }
}

Mais je ne peux pas vraiment comprendre celui-ci. Je sais que cet algorithme ne résoudra pas le problème car le nombre sera nul si la seconde instruction if n'est pas vraie.

Quelqu'un pourrait-il m'aider sur celui-ci?

OghmaOsiris
la source
Comment l'opérateur de comparaison est-il <défini sur les nœuds?
Joe
Voulez-vous calculer le nombre même s'il ne s'agit pas d'un arbre de recherche binaire?
Joe
1
Votre algorithme est-il censé renvoyer quelque chose, comme trueou false?
Joe
2
Vous devriez peut-être essayer de définir d'abord deux fonctions distinctes: une pour vérifier s'il s'agit d'un BST et une pour compter les branches complètes. Cela devrait être plus facile à gérer.
sepp2k
1
@OghmaOsiris J'imagine qu'il a dit cela parce que la question est essentiellement "Voici mon code, comment puis-je le faire fonctionner?". Si le code n'était pas de la variété pseudo (ish), ce serait certainement une question SO.
sepp2k

Réponses:

10

Comme d'autres l'ont déjà indiqué dans les commentaires, vous avez vraiment deux fonctions non liées ici: tester si l'arborescence est une arborescence de recherche et compter les branches complètes. À moins que la mission ne l'exige spécifiquement, j'écrirais deux fonctions distinctes.

Voyons d'abord compter les branches complètes. Cela signifie compter les nœuds qui ont à la fois un enfant gauche et un enfant droit. Ensuite, vous devez incrémenter le compteur ( count = count + 1) lorsque les deux T.leftet ne T.rightsont pas nuls (pas T.left.dataet T.right.data: les données n'ont pas d'importance pour cette tâche).

if (T.left and T.right) {
    count = count + 1

En outre, vous devez explorer le sous-arbre gauche même si le sous-arbre droit est vide, et vous devez explorer le sous-arbre droit même si le sous-arbre gauche est vide. Regardez donc où vous placez les appels récursifs.

Pour tester si l'arborescence est une arborescence de recherche, vous devez inspecter les valeurs des données. Vous avez déjà quelque chose de proche de la bonne comparaison; pas tout à fait juste. Écrivez quelques exemples d'arbres de formes variées (pas très gros, 2 à 5 nœuds) et exécutez votre algorithme dessus pour voir ce qui se passe.

Vous devez encore trouver un endroit où mettre le résultat du contrôle de validité. Encore une fois, regardez où vous placez les appels récursifs (si vous ne faites que cette partie, il existe plusieurs solutions, mais à ce stade, ne vous inquiétez pas si vous n'en voyez qu'une).

Enfin, une fois que vous avez réussi à écrire les deux fonctions séparément et que vous les avez testées sur quelques exemples, assemblez-les soigneusement (si la tâche l'exige).

Gilles 'SO- arrête d'être méchant'
la source
merci, j'ai relu la question et c'était censé être des méthodes séparées.
OghmaOsiris
7

Dans des choses comme celle-ci, il est souvent plus facile de penser à l'envers, alors pensez d'abord à ce dont vous avez besoin. À partir de votre description, listons-les:

  • Récursivité
  • Validité
  • Nombre de nœuds complets

OK, c'est une liste assez courte, cela devrait être gérable. Commençons par une méthode vide et j'ajouterai une description de ce qui devrait se passer.

valid_bst () {
}

Maintenant validité. Comment vérifiez-vous la validité? Dans le chat, vous avez dit qu'un arbre est valide "si ... tous les enfants de gauche sont inférieurs au parent et les enfants de droite sont supérieurs au parent". Je suis sûr que vous vouliez également permettre l'égalité. Ce serait t.left.value <= t.value <= t.right.value.

valid_bst () {
    This node is valid if t.left.value <= t.value <= t.right.value
}

Mais que faire si l'un des enfants est porté disparu? D'après ce que vous avez dit, je pense que vous savez que le nœud est toujours valide s'il en manque un (ou les deux). Ajoutons ceci, restructurant légèrement:

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
}

OK, nous savons maintenant si ce nœud est valide. Comment vérifions-nous si l’arbre entier est valide? Ce n'est pas dans un tableau, donc nous ne pouvons / ne voulons probablement pas le boucler linéairement. Votre mission donne la réponse: récursivité. Mais comment accumuler une réponse en utilisant la récursivité? Nous avons accès à trois informations, si ce nœud est valide, et le résultat d'appels demandant si les nœuds gauche et droit sont valides. Évidemment, l'arbre n'est valide que si les trois sont vraies.

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
    Is the left child valid?
    Is the right child valid?
    This tree is only valid if this node and both its children are.
}

Si vous faites attention, cela nous dit même ce que notre fonction doit retourner.

Maintenant, comment intégrons-nous le comptage? Vous dites ce qui compte ("un nœud parent avec des nœuds enfants gauche et droit"), et cela ne devrait pas être difficile à traduire en code réel. Vérifiez si cette condition est remplie et incrémentez le compteur de manière appropriée. N'oubliez pas que cela doit être quelque part où il sera atteint chaque fois que cela sera vrai.

Et bien sûr, j'ai omis certains détails comme la condition d'arrêt de récursivité et vérifie la valeur null.

Kevin
la source
6

Mes trois commentaires ci-dessus sont trois indices de problèmes avec votre code.

  1. À moins que vous n'ayez déjà défini spécifiquement comment un opérateur de comparaison doit gérer le type de données de nœud, la comparaison directe de deux nœuds ne fera probablement pas ce que vous voulez. Vous vouliez probablement comparer les champs stockés dans les nœuds, par exemplenode1.value < node2.value
  2. en ce moment, vous n'augmentez le nombre que si le troisième ifest vrai, êtes-vous sûr que c'est ce que vous vouliez faire? Par ailleurs, vous voudrez peut-être vérifier que cette instruction if fait ce que vous voulez.
  3. Je suppose que vous voulez retourner vrai si l'arbre est un BST valide et faux sinon. Ce qui signifie que vous devez toujours retourner vrai ou faux dans un cas de base, et vous devriez également renvoyer les résultats de vos appels récursifs.
Joe
la source
Concernant le premier point: c'est du pseudo-code, non? Donc, tant que l'intention est transmise au lecteur, il n'y a aucune raison de définir des choses comme ça.
sepp2k
@ sepp2k c'est vrai, et mon commentaire est probablement un peu trop pointilleux pour le pseudo-code. Je suppose que mon point est que nous devons comprendre ce que signifie comparer deux nœuds. Votre point est que nous devons déjà comprendre cela implicitement.
Joe
Exactement.
sepp2k