Fractionner dans l'arborescence AVL avec complexité

9

L'opération de fractionnement peut-elle être mise en œuvre pour les arbres AVL complexes O(logn)? Je suis intéressé par des liens vers des articles ou toute information spécifique sur ce sujet.

L'opération de division divise l'arborescence AVL en deux arborescences AVL dérivées, en fonction de la clé. L'un des arbres dérivés doit contenir tous les sommets dans lesquels toutes les clés sont inférieures à la clé d'origine et le second les autres.

Je sais que cela peut se faire en O(log2n)temps. Voici un lien vers la mise en œuvre avec complexitéO(log2n): https://code.google.com/p/self-balancing-avl-tree/

Je sais également comment fusionner deux arbres AVL, de sorte que les clés d'un arbre sont toutes plus petites que les clés de l'autre, dans O(logn)temps. Voici une implémentation complexeO(logn):

def Merge(l, r) {
    if (!l || !r) 
        return l ? l : r;
    if (l->h <= r->h)
        r->l = Merge(l, r->l), Rebalance(r);
    else
        l->r = Merge(l->r, r), Rebalance(l);
}
Denis Galeev
la source

Réponses:

7

Oui, c'est possible.

Vous pouvez en lire plus dans Ramzi Fadel et Kim Vagn Jakobsen "Structures de données et algorithmes dans une mémoire à deux niveaux", section 3.1.6 , (miroir) ou dans la bibliothèque standard OCaml, à la fonction "split".

L'une des informations clés est que la fonction de fusion que vous mentionnez est, avec une comptabilité plus prudente, O(h1h2), où h1 est la hauteur de l'arbre le plus grand et h2est la hauteur de l'arbre le plus court. En tant que tel, la fusion d'une liste d'arbres ayant des hauteurs décroissantes ou ascendantes ne coûte queO(hmaxhmin), depuis la somme des télescopes .

jbapple
la source
-1

Définissons une fonction split(T,v)qui prend dans un arbre, Tet une valeur de scission à, v. Supposons que chaque nœud de l'arborescence stocke son enfant gauche et son enfant droit en plus de la valeur de ce nœud. Utilisez l'algorithme suivant:

  1. Nous vérifions d'abord si l'arbre d'entrée est simplement une feuille ou non.

  2. Si Tn'est pas une feuille, comparez la valeur de son nœud racine v'avec v.

  3. Si c'est v' < valors récursivement appeler split sur le sous-arbre gauche. Stockez les valeurs de l'appel récursif sous L'(arborescence renvoyée à gauche), R'(renvoyée arborescence à droite) et r(type d'option indiqué si la valeur a vété trouvée ou non). Construisez le nouvel arbre de droite newR = Node(R',v',R), et revenez (L',r,newR).

  4. Sinon, v' > vappelez alors récursivement split sur le sous-arbre droit. Stockez les valeurs de l'appel récursif sous L'(arborescence renvoyée à gauche), R'(renvoyée arborescence à droite) et r(type d'option indiqué si la valeur a vété trouvée ou non). Construisez le nouvel arbre de gauche newL = Node(L',v',L), et revenez (newL,r,R').

  5. Sinon v' = v, revenez L, SOME(v), R.

  6. Si Test une feuille, nous devons avoir atteint la racine de l'arbre sans trouver la valeur d'entrée v à diviser. Revenez que vous n'avez pas trouvé la feuille en repassant NONE.

Pourquoi est-ce logarithmique? Eh bien, vous ne traversez qu'un seul chemin de la racine à la feuille de l'arbre, tout au plus. Nous pouvons facilement reconstruire des nœuds en temps constant car nous ne faisons que réaffecter des références (dans un langage impératif) ou réaffecter des valeurs qui prennent un temps constant à générer (dans un langage fonctionnel ).O(logn)O(logn)

Voici le code correspondant à l'algorithme. C'est écrit en SML, mais je serais prêt à clarifier ce que quelque chose signifie dans les commentaires.

fun split(T,v) = case T of Leaf => (Leaf, NONE, Leaf) | Node(L,v,R) => case compare(v, v') of LESS => let val (L',r,R') = split(L,k) in (L',r,Node(R',r,R)) end | GREATER => let val (L',r,R') = split(R,k) in (Node(L',v',L),r,R') end | EQUAL => (L, SOME(v), R)

Consultez ce document pour plus de détails. Il fournit une explication plus approfondie de ce qui précède.

Bharadwaj Ramachandran
la source
Cela ne concerne pas les conditions d'équilibre AVL.
jbapple