web-dev-qa-db-fra.com

Comment savez-vous où effectuer des rotations dans une arborescence AVL?

Je suis donc auto-enseignant des arbres AVL et je comprends l'idée de base derrière cela, mais je veux juste m'assurer que mon intuition de le mettre en œuvre est valide:

Je vais l'examiner avec la rotation à gauche-

Ainsi, la situation suivante est simple:

      8
     / \
    7   10
   /
  6
 /
3

Lorsque nous ajoutons le 3, l'arbre se rééquilibre pour:

    8
   / \
  6   10
 / \
3   7

Mais la rotation est-elle basée sur l'addition du 3 ou le déséquilibre du sous-arbre enraciné à 7? Est-ce même basé sur le déséquilibre de l'arbre enraciné à 8?

L'exemple suivant est où les choses deviennent un peu velues, à mon avis:

      9
     / \
    7   10
   / \
  6   8
 /
3

Donc, dans ce cas, le sous-arbre à 7 est correct lorsque le 3 est ajouté, de sorte que le sous-arbre n'a pas besoin de tourner. Cependant, l'arbre à 9 est déséquilibré avec l'ajout de 3, donc nous basons la rotation à 9. Nous obtenons:

      7
     / \
    6   9
   /   / \
  3   8   10

Donc, en écrivant mon code, ce que je ferai très bientôt, le code suivant, à partir de petits sous-arbres travaillant jusqu'à des sous-arbres plus gros, ferait-il l'affaire?

pseudocode:

function balanceTree(Node n){

  if (n is not null){

    balanceTree(n.rightchild);
    balanceTree(n.leftchild);
  }

  if (abs(balanceFactor(n))>1){

    rotateAsNeeded(n);// rotate based on balance factor

  }

}

Merci d'avance!

30
Skorpius

Le pseudocode que vous avez publié équilibrera correctement un arbre. Cela dit, c'est trop inefficace pour être pratique - notez que vous explorez récursivement l'arborescence entière en essayant de faire des opérations de rééquilibrage, ce qui fera que toutes les insertions et suppressions prendront O(n) temps, ronger tous les gains d'efficacité d'avoir un arbre équilibré.

L'idée derrière les arbres AVL est que globalement le rééquilibrage de l'arbre peut être fait en appliquant itérativement local rotations. En d'autres termes, lorsque vous effectuez une insertion ou une suppression et que vous devez effectuer des rotations d'arbre, ces rotations n'apparaîtront pas à des endroits aléatoires dans l'arbre. Ils apparaîtront toujours le long du chemin d'accès que vous avez emprunté lors de l'insertion ou de la suppression du nœud.

Par exemple, vous étiez curieux d'insérer la valeur 3 dans cet arbre:

      9
     / \
    7   10
   / \
  6   8

Commençons par écrire la différence de facteurs d'équilibre associée à chaque nœud (il est essentiel que les nœuds d'arbre AVL stockent ces informations, car c'est ce qui permet de faire des insertions et des suppressions efficacement):

           9(+1)
         /       \
       7 (0)    10 (0)
      / \
  6(0)   8(0)

Voyons maintenant ce qui se passe lorsque nous insérons 3. Cela place le 3 ici:

           9(+1?)
          /       \
        7 (0?)    10 (0)
       /   \
   6(0?)   8(0)
   /
 3(0)

Notez que j'ai marqué tous les nœuds du chemin d'accès avec un?, Car nous ne savons plus quels sont leurs facteurs d'équilibre. Puisque nous avons inséré un nouvel enfant pour 6, cela change le facteur d'équilibre pour le nœud 6 à +1:

           9(+1?)
          /       \
        7 (0?)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

De même, le sous-arbre gauche de 7 a augmenté en hauteur, donc son facteur d'équilibre doit être incrémenté:

           9(+1?)
          /       \
        7 (+1)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

Enfin, le sous-arbre gauche de 9 a augmenté de un, ce qui donne ceci:

           9(+2!)
          /       \
        7 (+1)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

Et ici, nous constatons que 9 a un facteur d'équilibre de +2, ce qui signifie que nous devons faire une rotation. Consulting le grand tableau de Wikipedia de toutes les rotations d'arbres AVL , nous pouvons voir que nous sommes dans le cas où nous avons un facteur d'équilibre de +2 où l'enfant gauche a un facteur d'équilibre de +1. Cela signifie que nous faisons une rotation à droite et tirons le 7 au-dessus du 9, comme indiqué ici:

        7(0)
       /   \
   6(+1)     9(0)
   /       /   \
 3(0)    8(0)   10 (0)

Et voilà! L'arbre est maintenant équilibré.

Notez que lorsque nous avons effectué cette procédure de correction, nous n'avons pas eu à regarder l'ensemble de l'arborescence. Au lieu de cela, tout ce que nous devions faire était de regarder le long du chemin d'accès et de vérifier chaque nœud là-bas. En règle générale, lors de la mise en œuvre d'une arborescence AVL, votre procédure d'insertion effectuera les opérations suivantes:

  • Si l'arbre est nul:
    • Insérez le nœud avec le facteur d'équilibre 0.
    • Renvoyez que la hauteur de l'arbre a augmenté de 1.
  • Autrement:
    • Si la valeur à insérer correspond au nœud actuel, ne faites rien.
    • Sinon, insérez récursivement le nœud dans la sous-arborescence appropriée et obtenez la quantité de changement de hauteur d'arbre.
    • Mettez à jour le facteur d'équilibre de ce nœud en fonction de la quantité de hauteur de sous-arbre modifiée.
    • Si cela nécessite une série de rotations, effectuez-les.
    • Renvoie la modification résultante de la hauteur de cet arbre.

Puisque toutes ces opérations sont locales, le travail total effectué est basé uniquement sur la longueur du chemin d'accès, qui dans ce cas est O (log n) car les arbres AVL sont toujours équilibrés.

J'espère que cela t'aides!


PS: Votre exemple initial était cet arbre:

      8
     / \
    7   10
   /
  6
 /
3

Notez que cet arbre n'est pas réellement un arbre AVL légal, car le facteur d'équilibre du nœud racine est +2. Si vous maintenez constamment l'équilibre de l'arbre en utilisant l'algorithme AVL, vous ne rencontrerez jamais ce cas.

33
templatetypedef