Dans une interview aujourd'hui, on m'a posé la question.
En plus de répondre en inversant la liste et en traversant à la fois en avant et en arrière, il y avait quelque chose de "fondamental" dans lequel l'intervieweur insistait. J'ai abandonné et bien sûr après l'entretien j'ai fait un peu de recherche. Il semble que l'insertion et la suppression soient plus efficaces dans une liste à double liaison que dans une liste à liaison unique. Je ne sais pas trop comment cela peut être plus efficace pour une liste doublement chaînée, car il est évident que davantage de références sont nécessaires pour changer. Quelqu'un peut-il expliquer le secret derrière? Honnêtement, j'ai fait pas mal de recherches et j'ai échoué à comprendre, mon principal problème étant le fait qu'une recherche O(n) est toujours nécessaire pour la liste à double lien).
L'insertion est clairement moins de travail dans une liste à liaison unique, tant que vous vous contentez de toujours insérer en tête ou après un élément connu. (Autrement dit, vous ne pouvez pas insérer avant un élément connu, mais voir ci-dessous.)
La suppression, en revanche, est plus délicate car vous devez connaître l'élément avant l'élément à supprimer.
Une façon de procéder consiste à faire fonctionner l'API de suppression avec le prédécesseur de l'élément à supprimer. Cela reflète l'API d'insertion, qui prend l'élément qui sera le prédécesseur du nouvel élément, mais ce n'est pas très pratique et c'est difficile à documenter. C'est généralement possible, cependant. D'une manière générale, vous arrivez à un élément d'une liste en parcourant la liste.
Bien sûr, vous pouvez simplement rechercher dans la liste depuis le début pour trouver l'élément à supprimer, afin de savoir quel était son prédécesseur. Cela suppose que l'API de suppression inclut la tête de la liste, ce qui est également gênant. De plus, la recherche est stupidement lente.
La façon dont presque personne n'utilise, mais qui est en fait assez efficace, est de définir un itérateur de liste à liaison unique pour être le pointeur sur l'élément précédant la cible actuelle de l'itérateur. C'est simple, une seule indirection plus lente que l'utilisation d'un pointeur directement sur l'élément, et rend l'insertion et la suppression rapides. L'inconvénient est que la suppression d'un élément peut invalider d'autres itérateurs pour répertorier les éléments, ce qui est ennuyeux. (Cela n'invalide pas l'itérateur de l'élément en cours de suppression, ce qui est bien pour les traversées qui suppriment certains éléments, mais ce n'est pas beaucoup de compensation.)
Si la suppression n'est pas importante, peut-être parce que les infrastructures de données sont immuables, les listes à liaison unique offrent une autre propriété vraiment utile: elles permettent le partage de structure. Une liste à liaison unique peut heureusement être la queue de plusieurs têtes, ce qui est impossible pour une liste à double liaison. Pour cette raison, les listes liées individuellement sont traditionnellement la simple structure de données de choix pour les langages fonctionnels.
Voici un code qui m'a rendu plus clair ... Ayant:
class Node{
Node next;
Node prev;
}
SUPPRIMER un nœud dans une LISTE À LIENS UNIQUES -O (n) -
Vous ne savez pas quel est le nœud précédent, vous devez donc parcourir la liste jusqu'à ce que vous le trouviez:
deleteNode(Node node){
prevNode = tmpNode;
tmpNode = prevNode.next;
while (tmpNode != null) {
if (tmpNode == node) {
prevNode.next = tmpNode.next;
}
prevNode = tmpNode;
tmpNode = prevNode.next;
}
}
SUPPRIMER un nœud dans une LISTE DOUBLE LIEN -O (1) -
Vous pouvez simplement mettre à jour les liens comme celui-ci:
deleteNode(Node node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
Voici mes réflexions sur la liste à double liaison:
Vous avez un accès prêt\insert aux deux extrémités.
il peut fonctionner comme une file d'attente et une pile en même temps.
La suppression de nœuds ne nécessite aucun pointeur supplémentaire.
Vous pouvez appliquer la traversée Hill-Climb puisque vous avez déjà accès aux deux extrémités.
Si vous stockez des valeurs numériques et que votre liste est triée, vous pouvez conserver un pointeur/une variable pour la médiane, puis l'opération de recherche peut être hautement optimale en utilisant l'approche statistique.
Si vous allez supprimer un élément dans une liste chaînée, vous devrez lier l'élément précédent à l'élément suivant. Avec une liste doublement liée, vous avez un accès facile aux deux éléments car vous avez des liens vers les deux.
Cela suppose que vous disposez déjà d'un pointeur sur l'élément à supprimer et qu'aucune recherche n'est impliquée.
"En plus de répondre en inversant la liste et en traversant en avant et en arrière, il y avait quelque chose de" fondamental "".
Personne ne semble avoir mentionné: dans une liste doublement liée, il est possible de réinsérer un élément supprimé simplement en ayant un pointeur sur l'élément supprimé. Voir l'article de Knuth's Dancing Links. Je pense que c'est assez fondamental.
Doubly Linked list is more effective than the Singly linked list when the location of the element to be deleted is given.Because it is required to operate on "4" pointers only & "2" when the element to be deleted is at the first node or at the last node.
struct Node {
int Value;
struct Node *Fwd;
struct Node *Bwd;
);
Only the below line of code will be enough to delete the element ,If the element to be deleted is not in the first or last node.
X->Bwd->Fwd = X->Fwd; X->Fwd->Bwd = X->Bwd ;