Série 7 – Ensembles par arbres de recherche

Introduction

Le but de cette série, la troisième du mini-projet collections, est de terminer la classe TreeSet qui met en œuvre un ensemble au moyen d'un arbre binaire de recherche.

Pour démarrer, nous vous fournissons une archive Zip contenant la totalité du code donné dans le cours, sous une forme légèrement améliorée.

Exercice 1 – tests

Avant de commencer à compléter la classe TreeSet, il convient de créer une classe de test pour elle.

Cela devrait se faire facilement puisque la classe abstraite SetTest, fournie et déjà utilisée la semaine dernière, contient des méthodes de test pour n'importe quelle classe implémentant l'interface Set. Seule une de ses méthodes est abstraite, newSet, son but étant de créer une instance de la classe d'ensemble à tester.

Normalement, il suffirait donc de définir une nouvelle sous-classe de SetTest, nommée TreeSetTest, et d'écrire le code (trivial) de la méthode newSet pour obtenir une classe de test pour TreeSet. Malheureusement, en essayant de faire cela, vous rencontrerez un problème nécessitant une légère adaptation de la classe SetTest et de ses sous-classes.

Faites cette adaptation et exécutez ensuite votre classe TreeSetTest comme test JUnit. Seuls trois tests sur 13 devraient s'exécuter correctement.

Exercice 2 – ajout

Ecrivez le code de la méthode d'ajout, add, en vous basant sur la description donnée dans le cours et en vous aidant de la méthode auxiliaire nodeOrParentFor. Utilisez de plus à bon escient les différents constructeurs de la classe Node qui vous permettent de simplifier votre code.

Comme toujours, relancez les tests après avoir écrit votre méthode afin de vérifier que ceux qui la concernent passent désormais.

Exercice 3 – suppression

Ecrivez le code de la méthode de suppression, remove, en vous basant sur la description donnée dans le cours. Aidez-vous cette fois des méthodes nodeFor et successor. Notez que la méthode successor fournie est plus générale — donc son code plus complexe — que celle donnée dans le cours, puisqu'elle fonctionne également dans le cas où le nœud qu'on lui passe n'a pas de fils droit. Cela n'est pas nécessaire pour la méthode remove, mais très utile pour mettre en œuvre l'itération.

Exercice 4 – itérateur

Ecrivez le code de la méthode d'itération, iterator.

L'itérateur retourné par cette méthode n'est pas aussi difficile à écrire qu'il n'y paraît. En effet, la méthode successor mentionnée ci-dessus retourne le successeur de n'importe quel nœud. C'est-à-dire que, étant donné un nœud, elle retourne le nœud contenant le plus petit élément plus grand que celui du nœud en question, ou null si ce nœud contient le plus grand élément de l'ensemble.

Ainsi, l'itérateur peut conserver une référence vers le prochain nœud à retourner lors de l'itération. Initialement, elle désigne le nœud contenant le plus petit élément de l'ensemble, c-à-d celui situé « en bas à gauche » de l'arbre. Ensuite, à chaque appel à next, un simple appel à successor permet de mettre à jour cette référence.

Notez au demeurant que, comme cela a été dit au cours, cet itérateur produit les éléments de l'ensemble dans l'ordre.

Pour mettre en œuvre la méthode remove de l'itérateur, il faut conserver une référence vers le dernier nœud visité par next. Sa suppression se fait facilement si on prend soin d'extraire la plus grosse partie de la méthode remove de la classe TreeSet dans une méthode auxiliaire nommée p.ex. removeNode et supprimant un nœud donné. Une fois cela fait, la méthode remove de l'itérateur peut simplement appeler removeNode sur le dernier nœud visité par next.