web-dev-qa-db-fra.com

Suppression d'un nœud intermédiaire d'une liste liée unique lorsque le pointeur sur le nœud précédent n'est pas disponible

Est-il possible de supprimer un nœud intermédiaire dans la liste chaînée unique lorsque les seules informations disponibles sont le pointeur vers le nœud à supprimer et non le pointeur vers le nœud précédent? Après la suppression, le nœud précédent doit pointer vers le nœud à côté de nœud supprimé.

40
Nitin

C'est certainement plus un quiz plutôt qu'un vrai problème. Cependant, si nous sommes autorisés à faire une hypothèse, elle peut être résolue en O(1) temps. Pour ce faire, les restrictions que la liste pointe vers doivent être copiables. L'algorithme est comme le Suivant:

Nous avons une liste qui ressemble à: ... -> Node (i-1) -> Node (i) -> Node (i + 1) -> ... et nous devons supprimer Node (i).

  1. Copiez les données (pas le pointeur, les données elles-mêmes) de Node (i + 1) vers Node (i), la liste ressemblera à: ... -> Node (i-1) -> Node (i + 1) -> Noeud (i + 1) -> ...
  2. Copiez le NEXT du deuxième nœud (i + 1) dans une variable temporaire.
  3. Maintenant, supprimez le deuxième nœud (i + 1), il ne nécessite pas de pointeur sur le nœud précédent.

Pseudocode:

void delete_node(Node* pNode)
{
    pNode->Data = pNode->Next->Data;  // Assume that SData::operator=(SData&) exists.
    Node* pTemp = pNode->Next->Next;
    delete(pNode->Next);
    pNode->Next = pTemp;
}

Mike.

87
Mikhail Tatarnikov

Supposons une liste avec la structure

A -> B -> C -> D

Si vous n'aviez qu'un pointeur sur B et que vous vouliez le supprimer, vous pourriez faire quelque chose comme

tempList = B->next;
*B = *tempList;
free(tempList);

La liste ressemblerait alors à

A -> B -> D

mais B conserverait l'ancien contenu de C, supprimant effectivement ce qui était en B. Cela ne fonctionnera pas si un autre morceau de code contient un pointeur vers C. Cela ne fonctionnera pas si vous essayez de supprimer le nœud D. Si vous souhaitez effectuer ce type d'opération, vous devrez créer la liste avec un nœud de queue factice qui n'est pas vraiment utilisé, vous garantissez donc qu'aucun nœud utile n'aura de pointeur NULL suivant. Cela fonctionne également mieux pour les listes où la quantité de données stockées dans un nœud est petite. Une structure comme

struct List { struct List *next; MyData *data; };

serait OK, mais celui où il est

struct HeavyList { struct HeavyList *next; char data[8192]; };

serait un peu lourd.

26
Ben Combee

Pas possible.

Il existe des hacks pour imiter la suppression.

Mais rien de tout cela ne supprimera réellement le nœud vers lequel le pointeur pointe.

La solution populaire de suppression du nœud suivant et de copie de son contenu dans le nœud réel à supprimer a des effets secondaires si vous avez pointeurs externes pointant aux noeuds de la liste, auquel cas un pointeur externe pointant vers le noeud suivant deviendra suspendu.

11
codaddict

J'apprécie l'ingéniosité de cette solution (suppression du nœud suivant), mais elle ne répond pas à la question du problème. Si c'est la solution réelle, la bonne question devrait être "supprimer de la liste chaînée la VALEUR contenue dans un nœud sur lequel le pointeur est donné". Mais bien sûr, la bonne question vous donne un indice sur la solution. Le problème tel qu'il est formulé vise à confondre la personne (ce qui m'est en fait arrivé, notamment parce que l'intervieweur n'a même pas mentionné que le nœud est au milieu).

5
Alex B

Une approche consisterait à insérer un null pour les données. Chaque fois que vous parcourez la liste, vous gardez une trace du nœud précédent. Si vous trouvez des données nulles, vous corrigez la liste et passez au nœud suivant.

4
Joe Hildebrand

La meilleure approche consiste toujours à copier les données du nœud suivant dans le nœud à supprimer, à définir le pointeur suivant du nœud sur le pointeur suivant du nœud suivant et à supprimer le nœud suivant.

Les problèmes de pointeurs externes pointant vers le nœud à supprimer, tout en étant vrai, se poseraient également pour le nœud suivant. Considérez les listes liées suivantes:

A-> B-> C-> D-> E-> F et G-> H-> I-> D-> E-> F

Dans le cas où vous devez supprimer le nœud C (dans la première liste chaînée), par l'approche mentionnée, vous supprimerez le nœud D après avoir copié le contenu sur le nœud C. Cela se traduira par les listes suivantes:

A-> B-> D-> E-> F et G-> H-> I-> pointeur pendant.

Si vous supprimez complètement le NODE C, les listes résultantes seront:

A-> B-> D-> E-> F et G-> H-> I-> D-> E-> F.

Cependant, si vous devez supprimer le nœud D et que vous utilisez l'approche antérieure, le problème des pointeurs externes est toujours là.

4
Sandeep Mathias

La suggestion initiale était de transformer:

a -> b -> c

à:

a ->, c

Si vous conservez les informations autour, par exemple, d'une carte de l'adresse du nœud à l'adresse du nœud suivant, vous pouvez corriger la chaîne la prochaine fois pour parcourir la liste. Si vous devez supprimer plusieurs éléments avant la prochaine traversée, vous devez suivre l'ordre des suppressions (c'est-à-dire une liste de modifications).

La solution standard consiste à considérer d'autres structures de données comme une liste de sauts.

3
Allan Wind

Peut-être faire une suppression logicielle? (c'est-à-dire, définir un indicateur "supprimé" sur le nœud) Vous pouvez nettoyer la liste plus tard si vous en avez besoin.

3
perimosocordiae

Donné

A -> B -> C -> D

et un pointeur vers, disons, l'élément B, vous le supprimeriez en
1. libérer toute mémoire appartenant aux membres de B
2. copier le contenu de C dans B (cela inclut son pointeur "suivant")
3. supprimer l'intégralité de l'élément C

Bien sûr, vous devrez faire attention aux cas Edge, comme travailler sur des listes d'un élément.

Maintenant où il y avait B, vous avez C et le stockage qui était autrefois C est libéré.

1
DarenW

Pas si vous souhaitez conserver la traversabilité de la liste. Vous devez mettre à jour le nœud précédent pour établir un lien avec le suivant.

Comment vous êtes-vous retrouvé dans cette situation, de toute façon? Qu'est-ce que vous essayez de faire qui vous fait poser cette question?

1
Allen

Vous devrez parcourir la liste pour trouver le nœud précédent. Cela fera la suppression en général O (n ** 2). Si vous êtes le seul code à effectuer des suppressions, vous pouvez faire mieux en pratique en mettant en cache le nœud précédent et en démarrant votre recherche là-bas, mais si cela aide dépend du modèle de suppressions.

1
Kimbo
void delself(list *list)
{
   /*if we got a pointer to itself how to remove it...*/
   int n;

   printf("Enter the num:");

   scanf("%d",&n);

   while(list->next!=NULL)
   {
       if(list->number==n) /*now pointer in node itself*/
       {
           list->number=list->next->number;
           /*copy all(name,rollnum,mark..) data of next to current, disconect its next*/
           list->next=list->next->next;
       }
       list=list->next;
   }
}
0
Aneesh

oui, mais vous ne pouvez pas le dissocier. Si vous ne vous souciez pas de corrompre la mémoire, allez-y ;-)

0
Steven A. Lowe

Le code suivant créera un LL, n demandera ensuite à l'utilisateur de donner le pointeur au nœud à supprimer. il affichera la liste après la suppression. Il fait la même chose qu'en copiant le nœud après le nœud à supprimer, sur le nœud à supprimer, puis supprimez le nœud suivant du nœud à supprimer. c'est à dire

a B c d

copiez c vers b et c gratuitement pour que le résultat soit

a-c-d

struct node  
{
    int data;
    struct node *link;
 };

void populate(struct node **,int);

void delete(struct node **);

void printlist(struct node **);

void populate(struct node **n,int num)
{

    struct node *temp,*t;
    if(*n==NULL)
    {
        t=*n;
        t=malloc(sizeof(struct node));
        t->data=num;
        t->link=NULL;
        *n=t;
    }
    else
    {
        t=*n;
        temp=malloc(sizeof(struct node));
        while(t->link!=NULL)
            t=t->link;
        temp->data=num;
        temp->link=NULL;
        t->link=temp;
    }
}

void printlist(struct node **n)
{
    struct node *t;
    t=*n;
    if(t==NULL)
        printf("\nEmpty list");

    while(t!=NULL)
    {
        printf("\n%d",t->data);
        printf("\t%u address=",t);
        t=t->link;
    }
}


void delete(struct node **n)
{
    struct node *temp,*t;
    temp=*n;
    temp->data=temp->link->data;
    t=temp->link;
    temp->link=temp->link->link;
    free(t);
}    

int main()
{
    struct node *ty,*todelete;
    ty=NULL;
    populate(&ty,1);
    populate(&ty,2);
    populate(&ty,13);
    populate(&ty,14);
    populate(&ty,12);
    populate(&ty,19);

    printf("\nlist b4 delete\n");
    printlist(&ty);

    printf("\nEnter node pointer to delete the node====");
    scanf("%u",&todelete);
    delete(&todelete);

    printf("\nlist after delete\n");
    printlist(&ty);

    return 0;
}
0

Oui, mais votre liste sera rompue après l'avoir supprimée.

Dans ce cas spécifique, parcourez à nouveau la liste et obtenez ce pointeur! En général, si vous posez cette question, il existe probablement un bogue dans ce que vous faites.

0
owenmarshall
void delself(list *list)
{
   /*if we got a pointer to itself how to remove it...*/
   int n;

   printf("Enter the num:");
   scanf("%d",&n);

   while(list->next!=NULL)
   {
      if(list->number==n) /*now pointer in node itself*/
      {
         list->number=list->next->number;   /*copy all(name,rollnum,mark..)
                             data of next to current, disconnect its next*/
         list->next=list->next->next;
      }
      list=list->next;
   }
}
0
Aneesh

Pour accéder à l'élément de liste précédent, vous devez parcourir la liste depuis le début jusqu'à ce que vous trouviez une entrée avec un pointeur next qui pointe vers votre élément actuel. Ensuite, vous auriez un pointeur sur chacun des éléments que vous auriez à modifier pour supprimer l'élément actuel de la liste - il suffit de définir previous->next à current->next puis supprimez current.

edit: Kimbo m'a battu en moins d'une minute!

0
Eltariel

Vous pouvez effectuer une dissociation différée à l'endroit où vous définissez les nœuds à supprimer de la liste avec un indicateur, puis les supprimer lors de la prochaine traversée appropriée. Les nœuds définis pour être dissociés devraient être correctement gérés par le code qui analyse la liste.

Je suppose que vous pouvez également parcourir à nouveau la liste depuis le début jusqu'à ce que vous trouviez la chose qui pointe vers votre élément dans la liste. À peine optimal, mais au moins une bien meilleure idée que la déconnexion différée.

En général, vous devez connaître le pointeur sur l'élément dont vous venez de provenir et vous devez le faire circuler.

(Edit: Ick, avec le temps qu'il m'a fallu pour taper une réponse complète, trois millions de personnes ont couvert presque tous les points que j'allais mentionner. :()

0
DJ Capelis

Vous avez la tête de liste, non? Vous venez de le traverser.

Disons que votre liste est pointée par "head" et le nœud pour la supprimer "del".

Pseudo-code de style C (les points seraient -> en C):

prev = head
next = prev.link

while(next != null)
{
  if(next == del)
  {
    prev.link = next.link;
    free(del);
    del = null;
    return 0;
  }
  prev = next;
  next = next.link;
}

return 1;
0
Charles Graham

Si vous avez une liste chaînée A -> B -> C -> D et un pointeur vers le nœud B. Si vous devez supprimer ce nœud, vous pouvez copier le contenu du nœud C dans B, le nœud D dans C et supprimer D. Mais nous ne pouvons pas supprimer le nœud en tant que tel dans le cas d'une liste à liaison unique car si nous le faisons, le nœud A sera également perdu. Bien que nous puissions revenir en arrière à A en cas de liste doublement liée.

Ai-je raison?

0
Smitha

C'est un morceau de code que j'ai mis en place qui fait ce que l'OP demandait, et peut même supprimer le dernier élément de la liste (pas de la manière la plus élégante, mais il le fait). Je l'ai écrit tout en apprenant à utiliser les listes chaînées. J'espère que cela aide.

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>

using namespace std;


struct node
{
    int nodeID;
    node *next;
};

void printList(node* p_nodeList, int removeID);
void removeNode(node* p_nodeList, int nodeID);
void removeLastNode(node* p_nodeList, int nodeID ,node* p_lastNode);

node* addNewNode(node* p_nodeList, int id)
{
    node* p_node = new node;
    p_node->nodeID = id;
    p_node->next = p_nodeList;
    return p_node;
}

int main()
{
    node* p_nodeList = NULL;
    int nodeID = 1;
    int removeID;
    int listLength;
    cout << "Pick a list length: ";
    cin >> listLength;
    for (int i = 0; i < listLength; i++)
    {
        p_nodeList = addNewNode(p_nodeList, nodeID);
        nodeID++;
    }
    cout << "Pick a node from 1 to " << listLength << " to remove: ";
    cin >> removeID;
    while (removeID <= 0 || removeID > listLength)
    {
        if (removeID == 0)
        {
            return 0;
        }
        cout << "Please pick a number from 1 to " << listLength << ": ";
        cin >> removeID;
    }
    removeNode(p_nodeList, removeID);
    printList(p_nodeList, removeID);
}

void printList(node* p_nodeList, int removeID)
{
    node* p_currentNode = p_nodeList;
    if (p_currentNode != NULL)
    {
        p_currentNode = p_currentNode->next;
        printList(p_currentNode, removeID);
        if (removeID != 1)
        {
            if (p_nodeList->nodeID != 1)
            {
                cout << ", ";
            }

            cout << p_nodeList->nodeID;
        }
        else
        {
            if (p_nodeList->nodeID !=2)
            {
                cout << ", ";
            }
            cout << p_nodeList->nodeID;
        }
    }
}

void removeNode(node* p_nodeList, int nodeID)
{
    node* p_currentNode = p_nodeList;
    if (p_currentNode->nodeID == nodeID)
    {
        if(p_currentNode->next != NULL)
        {
            p_currentNode->nodeID = p_currentNode->next->nodeID;
            node* p_temp = p_currentNode->next->next;
            delete(p_currentNode->next);
            p_currentNode->next = p_temp;
        }
        else
        {
            delete(p_currentNode);
        }
    }
    else if(p_currentNode->next->next == NULL)
    {
        removeLastNode(p_currentNode->next, nodeID, p_currentNode);
    }
    else
    {
        removeNode(p_currentNode->next, nodeID);
    }
}

void removeLastNode(node* p_nodeList, int nodeID ,node* p_lastNode)
{
    node* p_currentNode = p_nodeList;
    p_lastNode->next = NULL;
    delete (p_currentNode);
}
0
Desyn8686