Première partie
Objectifs d'apprentissage
- Concevoir une liste doublement chaînée possédant aussi un noeud factice.
- Modifier l'implémentation d'une liste chaînée afin d'y ajouter des méthodes.
- Résoudre des problèmes exigeant une bonne compréhension des interfaces.
Tableau circulaire
Les files sont un type abstrait de données reposants sur le principe du premier arrivé premier servi (Fisrt In First Out - FIFO). Leur implémentation à l'aide d'un tableau utilise une technique que l'on nomme tableau circulaire. Dans un tableau circulaire, on réutilise les cellules du début du tableau lorsque l'arrière de la file atteint la fin du tableau et que le tableau n'est pas rempli. Cette implémentation d'une file a une variable d'instance qui désigne l'avant de la file (head/front) et une pour l'arrière (tail/rear).
Lorsque l’on ajoute un élément en utilisant enqueue(), on ajoute un élément à la position arrière (tail) et on incrémente le pointeur tail (rear). Lorsque l’on enlève un élément en utilisant dequeue(), on enlève et retourne l’élément a la position avant (head) et incrémente le pointeur head (front)
Voici une représentation graphique d’un tableau circulaire. Nous pouvons soit la représenter comme un tableau ou le dernier élément est lié au premier, ou encore comme un tableau circulaire.
Figure 1 : Représentation visuelle des tableaux circulaires.
Les listes simplement et doublement chaînées
Rappel: Comme vu en classe, il existe plusieurs implémentations d'une liste avec des éléments chaînés. Nous vous avons notamment présenté les listes simplement et doublement chaînées en classe. Ces implémentations, bien que semblables possèdent des différences notables.
D'un côté, les listes simplement chaînées relient chaque élément à leur prochain, et ce, jusqu'au dernier élément qui aura pour suivant (next) null.
D'un autre côté, les listes doublement chaînées permettent l'implémentation plus rapide de certaines méthodes grâce au lien qui relie chaque élément à son précédent. Le dernier élément aura, telle la liste simplement chaînée, un prochain (next) null, tout le comme le premier élément qui a un précédent (prev) null.
Il y a aussi possibilité d'implémenter une liste simplement ou doublement chaînée avec un noeud factice, c'est-à-dire un noeud qui n'aura aucune valeur (null) et qui sera placé au début de la liste. Ce dernier permet de simplifier l'implémentation de méthodes en réduisant le nombre de cas spéciaux et rend aussi la liste circulaire puisque le premier élément de la liste aura ce noeud en élément précédent et le dernier élément l'aura en élément suivant.
Testez votre compréhension des différentes implémentations
Question 1.0 :
Décrivez la figure suivante. Notamment, s'agit-il d'une liste simplement ou doublement chaînée? Comment s'appelle la technique utilisée (un noeud ...)?
Voir l'imageQuestion 1.1 :
Vrai ou faux? Il est possible de parcourir une liste simplement chaînée dans les deux directions efficacement.
RéponseQuestion 1.2 :
Que devons-nous utiliser pour faciliter l'ajout d'un élément à la fin d'une liste?
RéponseQuestion 1.3 :
Vrai ou faux? Une liste circulaire possédant un noeud factice a plusieurs cas particuliers.
RéponseQuestion 1.4 :
Dans quel contexte l'utilisation de l'implémentation d'une liste à base de tableaux est-elle avantageuse à celle d'une liste à base d'éléments simplement chaînés? Nommez 2 méthodes.
RéponseQuestion 1.5 :
Avons-nous besoin d'une variable de référence tail dans le cas d'une liste circulaire doublement chaînée?
RéponseQuestion 1.6 :
Vrai ou faux? Pour implémenter une liste chaînée, nous pouvons tout simplement utiliser un tableau dynamique.
RéponseQuestion 1.7 :
Voici le constructeur de la classe imbriquée Node d'une List. Identifiez si la liste est simplement ou doublement chaînée.
//constructor
private Node(T value, Node next) {
this.value = value;
this.next = next;
}
RéponseQuestion 1.8 :
Qu'est-ce que le bout de code suivant effectue dans une liste circulaire doublement chaînée? Considérez que la variable current a été préalablement déclarée.
current = head.prev;
while (current != head) {
current = current.prev;
}
Réponse