Je ne comprends pas vraiment le diagramme que vous fournissez de l'arbre, parce que (contrairement au diagramme de liste lié) ils ne semblent pas avoir de relation avec les données stockées. En pratique, ils auront essentiellement les mêmes champs de données (Buffer, Start et Length) plus un, Size, qui est la taille totale des pièces dans le sous-arbre dirigé par le noeud. Au lieu des pointeurs précédent et suivant, ils auront des pointeurs gauche et droit (enfant). Et, bien sûr, ils auront des données supplémentaires nécessaires pour maintenir l'arbre équilibré (un bit rouge/noir dans le cas des arbres rouges/noirs, mais je ne pense pas que le mécanisme pour maintenir l'équilibre est important ici, vous pouvez utiliser des arbres AVL au lieu d'arbres rouges/noirs, par exemple, donc je vais ignorer cette partie du nœud ici
Le champ Taille est nécessaire pour trouver les données à un moment donné. offset (et par conséquent, pourrait être omis s'il n'y avait jamais besoin de faire une telle recherche.) Je pense que l'article lié mesure Taille en morceaux, alors que j'ai tendance à mesurer la taille en caractères (ou même octets), ce qui est Comme le note l'article lié, le champ Taille peut facilement être maintenu en temps logarithmique précisément parce qu'il rs à la taille du sous-arbre, plutôt qu'à son emplacement dans le flux de données.
Vous utilisez le champ Taille pour rechercher un nœud par décalage de tampon. Si le décalage est inférieur à la taille de l'enfant gauche, vous recurerez dans l'enfant gauche; si c'est au moins la longueur actuelle plus la taille de l'enfant gauche, vous soustrayez cette somme du décalage et recourez dans l'enfant droit. Sinon, le nœud actuel contient le décalage souhaité. Cela ne peut pas prendre plus de temps que la profondeur maximale de l'arbre, qui est O (log N) si l'arbre est raisonnablement équilibré.
je aussi un peu confus par votre schéma de liste chaînée, qui me semble représenterai le tampon He|!\0|y
, alors que je pense que ce soit He|y|!\0
:
------------------ ------------------- -------------------
Buffer = ORIGINAL| |Buffer = APPEND | |Buffer = ORIGINAL|
Start = 0 |<--|Start = 0 |<--|Start = 5 |
Length = 2 | |Length = 1 | |Length = 2 |
Next = 0x01 |-->|Next = 0x02 |-->|Next = NULL |
Previous = NULL | |Previous = 0x01 | |Previous = 0x01 |
------------------ ------------------- -------------------
L'arbre équilibré équivalent est:
-------------------
| Size = 5 |
| Buffer = APPEND |
| Start = 0 |
| Length = 1 |
-------------------
/ \
/ \
/ \
------------------- -------------------
|Size = 2 | |Size = 2 |
|Buffer = ORIGINAL| |Buffer = ORIGINAL|
|Start = 0 | |Start = 5 |
|Length = 2 | |Length = 2 |
------------------- -------------------
/ \ / \
/ \ / \
NULL NULL NULL NULL
l'algorithme pour trouver le noeud suivant dans l'ordre à partir d'un noeud donné est la suivante:
Alors que le pointeur enfant droit est NULL, revenez au parent. Continuez à vous déplacer vers le parent jusqu'à ce qu'un noeud soit trouvé dont le pointeur enfant droit n'est ni NULL ni l'enfant renvoyé.
Déplacez-vous vers le bon enfant.
Alors que le pointeur de l'enfant gauche n'est pas NULL, passez à l'enfant gauche
une application donnée de cet algorithme pourrait évidemment prendre O (log N) itérations des étapes 1 et/ou 3. Cependant , applications répétées (comme dans le cas où vous traversez le tampon dans un certain nombre de nœuds) sera linéaire au total car un lien donné (parent ⇆ enfant) sera traversé exactement deux fois, une fois dans chaque direction. Ainsi, le nombre total de liens parcourus si vous parcourez tout l'arbre est le double du nombre de liens dans l'arbre. (Et l'arbre a un moins lien que les noeuds, car il est un arbre.)
Si vous mesurez la taille des caractères, vous pouvez éviter la nécessité pour le champ « Longueur », parce que la longueur de la Les données directement référencées par un nœud sont simplement la différence entre la taille de sous-arbre du nœud et la somme de ses tailles de sous-arbres enfants. Cela peut (presque) réduire la taille d'un nœud à la taille du nœud de liste liée, en supposant que vous pouvez trouver un moyen de coder le bit rouge/noir (ou d'autres informations d'équilibrage) dans ce qui serait autrement des bits de remplissage. D'autre part, il est assez fréquent de voir des implémentations d'arbre binaire avec des pointeurs parents, ainsi que deux pointeurs enfants. (Il est clair que cela peut aider en regardant l'algorithme de cheminement ci-dessus.) Cependant, il n'est pas nécessaire de stocker les pointeurs parents car ils peuvent, par exemple, être conservés pendant un chemin d'arbre donné dans un tableau de pointeurs parents indexés par profondeur dans l'arbre. Ce tableau n'est évidemment pas plus grand que la profondeur maximale de l'arbre, donc un petit tableau (~ 50) de longueur fixe peut être utilisé.
Ces optimisations sont également bien au-delà de cette réponse.
@rici Je pense que je comprends surtout comment l'arbre fonctionne, je ne comprends tout simplement pas comment, dans ce contexte particulier (repeindre le tampon pour refléter les changements), il a un avantage. Wikipedia a une excellente explication, mais elle ne couvre pas ce sujet. Je n'ai pas trouvé d'autres ressources qui ont répondu à ma question, alors je l'ai demandé ici –
Je comprends cet aspect; Ce que je ne comprends pas, c'est comment les changements peuvent être reflétés en traversant l'arbre. En utilisant la liste doublement liée, c'est très clair; chaque nœud pointe vers un point dans le tampon d'origine et d'ajout, et en gardant une somme continue des longueurs de chaque entrée, toutes les modifications apportées à un nœud peuvent être reflétées en traversant la liste: ie, en choisissant d'imprimer des morceaux de texte une certaine position et longueur à partir du tampon d'origine ou du tampon d'ajout. Avec l'arbre, comment cela est accompli n'est pas clair pour moi. Cependant, je comprends la rotation/devoir "réparer" l'arbre. –
Je pense que mon plus gros problème est de lutter pour réfléchir pleinement à un exemple. Cela aiderait beaucoup à voir comment je pourrais construire un nouveau tampon, étant donné les informations stockées dans l'arbre (dans l'exemple ci-dessus, ou dans un autre exemple). Conceptuellement, il me semble que l'utilisation de la liste chaînée pour construire un tampon prendrait O (N). Mais l'opération équivalente ne prendrait-elle pas O (NlogN) pour l'arbre? Puisque, pour N pièces, il faut O (logN) pour récupérer ses informations? –