Oui, vous pouvez effectuer cette compression en temps , mais ce n'est pas facile :) Nous faisons d'abord quelques observations puis présentons l'algorithme. Nous supposons que l'arbre n'est pas compressé au départ - ce n'est pas vraiment nécessaire mais facilite l'analyse.O ( n logn )
Premièrement, nous caractérisons «l'égalité structurelle» de manière inductive. Soit et deux (sous) arbres. Si et sont tous les deux des arbres nuls (n'ayant aucun sommet), ils sont structurellement équivalents. Si et sont pas tous deux des arbres nuls, alors ils sont structurellement équivalents si leurs enfants gauches sont structurellement équivalents et leurs enfants droits sont structurellement équivalents. L '«équivalence structurelle» est le point fixe minimal sur ces définitions.T ′ T T ′ T T ′TT′TT′TT′
Par exemple, deux nœuds foliaires sont structurellement équivalents, car ils ont tous deux les arbres nuls comme leurs enfants, qui sont structurellement équivalents.
Comme il est assez ennuyeux de dire «leurs enfants de gauche sont structurellement équivalents et leurs enfants de droite aussi», nous dirons souvent «leurs enfants sont structurellement équivalents» et entendons la même chose. Notez également que nous disons parfois «ce sommet» lorsque nous voulons dire «le sous-arbre enraciné sur ce sommet».
La définition ci-dessus nous donne immédiatement un indice sur la façon d'effectuer la compression: si nous connaissons l'équivalence structurelle de tous les sous-arbres de profondeur au plus , alors nous pouvons facilement calculer l'équivalence structurelle des sous-arbres de profondeur . Nous devons faire ce calcul de manière intelligente pour éviter un temps d'exécution .d + 1 O ( n 2 )réré+ 1O ( n2)
L'algorithme attribuera des identifiants à chaque sommet lors de son exécution. Un identifiant est un nombre dans l'ensemble . Les identifiants sont uniques et ne changent jamais: nous supposons donc que nous définissons une variable (globale) à 1 au début de l'algorithme, et chaque fois que nous attribuons un identifiant à un sommet, nous attribuons la valeur actuelle de cette variable au sommet et l'incrémentons la valeur de cette variable.{ 1 , 2 , 3 , … , n }
Nous transformons d'abord l'arbre d'entrée en (au plus ) listes contenant des sommets d'égale profondeur, ainsi qu'un pointeur vers leur parent. Cela se fait facilement en temps .O ( n )nO ( n )
Nous compressons d'abord toutes les feuilles (nous pouvons trouver ces feuilles dans la liste avec des sommets de profondeur 0) en un seul sommet. Nous attribuons à ce sommet un identifiant. La compression de deux sommets se fait en redirigeant le parent de l'un des sommets pour pointer vers l'autre sommet à la place.
Nous faisons deux observations: premièrement, tout sommet a des enfants de profondeur strictement plus petite, et deuxièmement, si nous avons effectué une compression sur tous les sommets de profondeur inférieure à (et leur avons donné des identificateurs), alors deux sommets de profondeur sont structurellement équivalents et peut être compressé si les identifiants de leurs enfants coïncident. Cette dernière observation découle de l'argument suivant: deux sommets sont structurellement équivalents si leurs enfants sont structurellement équivalents, et après compression, cela signifie que leurs pointeurs pointent vers les mêmes enfants, ce qui signifie que les identifiants de leurs enfants sont égaux.dréré
Nous parcourons toutes les listes avec des nœuds de profondeur égale de petite profondeur à grande profondeur. Pour chaque niveau, nous créons une liste de paires entières, où chaque paire correspond aux identifiants des enfants de certains sommets de ce niveau. Nous avons que deux sommets de ce niveau sont structurellement équivalents si leurs paires entières correspondantes sont égales. En utilisant l'ordre lexicographique, nous pouvons les trier et obtenir les ensembles de paires entières qui sont égales. Nous compressons ces ensembles en sommets uniques comme ci-dessus et leur donnons des identificateurs.
Les observations ci-dessus prouvent que cette approche fonctionne et aboutit à l'arbre compressé. La durée totale d'exécution est plus le temps nécessaire pour trier les listes que nous créons. Comme le nombre total de paires entières que nous créons est , cela nous donne que le temps d'exécution total est , comme requis. Compter le nombre de nœuds qu'il nous reste à la fin de la procédure est trivial (il suffit de regarder le nombre d'identifiants que nous avons distribués).n O ( n log n )O ( n )nO ( n logn )