Recherche de noeuds dans la pile de débordements D'arbre binaire

j'ai utiliser la méthode suivante pour parcourir* un arbre binaire de 300 000 niveaux:

Node* find(int v){
   if(value==v)
      return this;
   else if(right && value<v)
      return right->find(v); 
   else if(left && value>v)
      return left->find(v);
}

cependant j'ai un défaut de segmentation dû au débordement de la pile. Des idées sur la façon de traverser l'arbre profond sans passer au-dessus des appels de fonction récursifs?

* Par "traverser", je veux dire "chercher un noeud avec une valeur donnée", pas "traverser l'arbre entier".

18
demandé sur Santiago Varela 2017-03-28 12:06:33

6 réponses

Oui! Pour un 300 000 niveau de l'arbre Eviter la récursion. Parcourez votre arbre et trouvez la valeur de manière itérative en utilisant une boucle.

recherche binaire représentation de L'arbre

           25             // Level 1
        20    36          // Level 2
      10 22  30 40        // Level 3
  .. .. .. .. .. .. .. 
.. .. .. .. .. .. .. ..   // Level n

juste pour clarifier davantage le problème. Ton arbre a une profondeur de n = 300.000 niveaux. Ainsi, dans le pire des cas, une Arbre De Recherche Binaire (BST) devront visiter des noeuds de l'arbre. C'est une mauvaise nouvelle parce que pire des cas algorithmique O (n) complexité temporelle. Un tel arbre peut avoir:

2ˆ300.000 noeuds = 9,9701 e+90308 noeuds (approximativement).


9.9701 e+90308 noeuds est un de façon exponentielle massive nombre de nœuds à visiter. Avec ces nombres, il devient si clair pourquoi la pile d'appels déborde.


la Solution (de façon itérative):

je suppose que votre noeud class/struct déclaration est un classique de la norme entier BST. Vous pouvez alors l'adapter et cela fonctionnera:

struct Node {
    int data;
    Node* right;
    Node* left;
};

Node* find(int v) {
    Node* temp = root;  // temp Node* value copy to not mess up tree structure by changing the root
    while (temp != nullptr) {
        if (temp->data == v) {
            return temp;
        }
        if (v > temp->data) {
            temp = temp->right;
        }
        else {
            temp = temp->left;
        }
    }
    return nullptr;
}

itératif approche évite les la récursivité, vous épargnant ainsi le tracas de devoir trouver récursivement la valeur dans un arbre si grand avec votre pile d'appels de programme.

27
répondu Santiago Varela 2017-04-16 22:39:35

une boucle simple où vous avez une variable de type noeud* que vous mettez au prochain noeud, puis boucle à nouveau ...

N'oubliez pas le cas que la valeur que vous recherchez n'existe pas!

9
répondu Rene 2017-03-28 09:12:18

vous pourriez implémenter la récursion en n'utilisant pas la pile d'appels mais une pile définie par l'utilisateur ou quelque chose de similaire; ceci pourrait être fait via le stack modèle. L'approche serait d'avoir un while boucle qui itère jusqu'à ce que la pile soit vide; comme l'implémentation existante utilise la recherche en profondeur-première recherche, l'élimination des appels récursifs peut être trouvée ici.

7
répondu Codor 2017-03-28 09:09:27

Lorsque l'arbre que vous avez est un Arbre De Recherche Binaire, et tout ce que vous voulez faire est de rechercher un noeud qui a une valeur spécifique, alors les choses sont simples: aucune récursion n'est nécessaire, vous pouvez le faire en utilisant une boucle simple comme d'autres l'ont fait remarquer.

Dans le cas plus général d'avoir un arbre qui n'est pas nécessairement Binaire Recherche arbre, et voulant exécuter un une traversée complète, le plus simple est d'utiliser la récursivité, mais comme vous comprenez déjà, si l'arbre est très profonde, puis la récursivité ne fonctionnera pas.

donc, pour éviter la récursion, vous devez implémenter une pile sur le tas C++. Vous devez déclarer un nouveau StackElement classe qui contiendra un membre pour chaque variable locale que votre fonction récursive originale avait, et un membre pour chaque paramètre que votre fonction récursive originale a accepté. (Vous pourriez être en mesure de vous en tirer avec moins de variables de membre, vous pouvez vous inquiéter de cela après que vous avez obtenu votre code de travail.)

vous pouvez stocker des instances de StackElement dans une collection stack, ou vous pouvez simplement avoir chacun d'eux contient un pointeur vers son parent, mettant ainsi pleinement en œuvre la pile par vous-même.

ainsi, au lieu de votre fonction s'appelant récursivement elle-même, elle se composera simplement d'une boucle. Votre fonction entre dans la boucle avec la StackElement être initialisé avec des informations sur le noeud racine de votre arbre. Ses parents pointeur sera nulle, ce qui est une autre façon de dire que la pile sera vide.

à chaque endroit où la version récursive de votre fonction s'appelait, votre nouvelle fonction attribuera une nouvelle instance de StackElement, en l'initialisant, et en répétant la boucle en utilisant cette nouvelle instance comme élément.

a chaque fois que la version récursive de votre fonction revient, votre nouvelle fonction va libérer le StackElement, popping celui qui était assis sur le haut de la pile, la nouvelle élément, et de répéter la boucle.

quand vous trouvez le noeud que vous cherchiez, vous cassez simplement de la boucle.

alternativement, si le noeud de votre arborescence existante supporte a) un lien vers son noeud "parent" et b) des données utilisateur (où vous pouvez stocker un drapeau "visité") alors vous n'avez pas besoin d'implémenter votre propre pile, vous pouvez simplement traverser l'arborescence en place: dans chaque itération de votre boucle vous vérifiez d'abord si le noeud courant est le noeud que vous cherchiez; si non, alors vous énumérez à travers les enfants jusqu'à ce que vous trouviez un qui n'a pas encore été visité, et puis vous le visitez; quand vous atteignez une feuille, ou un noeud dont les enfants ont tous été visités, puis vous faites marche arrière en suivant le lien vers le parent. Aussi, si vous avez la liberté de détruire l'arbre que vous traversez, alors vous n'avez même pas besoin de la notion de "données utilisateur": une fois que vous avez fini avec un nœud enfant, vous gratuit et de le rendre nul.

6
répondu Mike Nakis 2017-03-28 11:08:39

Eh bien, il peut être fait queue récursive au prix d'une seule variable locale supplémentaire et quelques comparaisons:

Node* find(int v){
  if(value==v)
    return this;
  else if(!right && value<v)
    return NULL;
  else if(!left && value>v)
    return NULL;
  else {
    Node *tmp = NULL;
    if(value<v)
      tmp = right;
    else if(value>v)
      tmp = left;
    return tmp->find(v);
  }
}
3
répondu user2793784 2017-03-28 23:36:12

marcher à travers un arbre binaire est un processus récursif, où vous continuerez à marcher jusqu'à ce que vous trouviez que le noeud que vous êtes à des points actuellement nulle part.

C'est que vous avez besoin d'une base appropriée condition. Quelque chose qui ressemble à:

if (treeNode == NULL)
   return NULL;

en général, traverser un arbre est accompli de cette façon (en C):

void traverse(treeNode *pTree){
  if (pTree==0)
    return;
  printf("%d\n",pTree->nodeData);
  traverse(pTree->leftChild);
  traverse(pTree->rightChild);
}
0
répondu 2017-03-28 09:27:27