سؤال

I am working on a function that would delete a node of a doubly-linked list. Here is my header file:

class LinkedList
{
private:
      struct Node
      {
         int data;
         Node *next;
         Node *previous;
      };

      int count;
      Node *head;
      Node *tail;

public:
      LinkedList() {head = NULL; tail = NULL; count = 0;} //Constructor

      void insert(const int );
      bool remove(const int );
      bool contains(const int );

      size_t lenght() {return count;}
};

My other functions work fine, but its my remove function that breaks on run-time. When i run my code i get a segmentation fault, and after 2 days of trying to figure out the flaw in my logic I am turning to the community for some help. I would be grateful for any feed-back at this point, thank you. Here is my remove function:

bool LinkedList::remove(const int item)
{//if the list is empty returns false
if(head == NULL) {return false;}

Node *hptr = head;
Node *tptr = tail;

if((hptr -> data) == item)
{//if the node is at the head of the list
  hptr = hptr -> next;
  delete head;
  hptr -> previous = NULL;
  head = hptr;
  --count;
  return true;

} else if((tptr -> data) == item) {
 //if the node is at the tail of the list
  tptr = tptr -> previous;
  delete tail;
  tail = tptr;
  tptr -> next = NULL;
  --count;
  return true;

} else {//if the node is in he middle of the list
  Node *ptr_head = head;   Node *ptr_headp = NULL;
  Node *ptr_tail = tail;   Node *ptr_tailp = NULL;

  while((ptr_head -> data) != item || (ptr_tail -> data) != item)
  {//pointers pass each other then data was not found
     if((ptr_tail -> data) < (ptr_head -> data)) {return false;}
   //traversing the list from the head and tail simultaniously
     ptr_headp = ptr_head;
     ptr_head = ptr_head -> next;

     ptr_tailp = ptr_tail;
     ptr_tail = ptr_tail -> previous;
  }

  if((ptr_head == ptr_tail) && ((ptr_tail -> data) == (ptr_head -> data)))
  {//the item is at the intersection of both head and tail pointers
     ptr_headp -> next = ptr_tailp;
     ptr_tailp -> previous = ptr_headp;
     delete ptr_head;
     delete ptr_tail;
     --count;
     return true;
  }

  if((ptr_head -> data) == item)
  {//the item is before middle node
     ptr_headp -> next = ptr_head -> next;
    (ptr_head -> next) -> previous = ptr_headp;
     delete ptr_head;
     --count;
     return true;
  }

  if((ptr_tail -> data) == item)
  {//the item is after the middle node
     ptr_tailp -> previous = ptr_tail -> previous;
    (ptr_tail -> previous) -> next = ptr_tailp;
     delete ptr_tail;
     --count;
     return true;
  }
}

return false;
}
هل كانت مفيدة؟

المحلول

This is a common example of a situation when changing the data structure a little could make the logic a lot simple by unifying the cases that otherwise look different *.

The main issue with the logic is that you have lots of conditions to check:

  • Deleting the first node that has other nodes after it
  • Deleting the last node that has other nodes preceding it
  • Deleting the only node
  • Deleting a node in the middle

You can make these four conditions identical to the last one by ensuring that there is always a node on the left and a node on the right of any node. Here is how you can do it:

class LinkedList
{
private:
      struct Node
      {
         int data;
         Node *next;
         Node *previous;
      };

      int count;
      // The change begins here
      Node headTail;
      // End of the change

public:
      LinkedList() {head = NULL; tail = NULL; count = 0;} //Constructor

      void insert(const int );
      bool remove(const int );
      bool contains(const int );

      size_t lenght() {return count;}
};

The head pointer is headTail's next; the tail pointer is its previous. Both next and previous point back to itself in an empty list.

This is a little inefficient, because the data of the headTail is unused. The list becomes circular, with one node always present. With this node in place, you can safely remove any node in the middle, and update the prior and the next pointers as if they belonged to different objects.


* Here is a link to an excellent reading not directly related to the problem at hand, but very useful to understanding the philosophy of this approach.

نصائح أخرى

// Locate the item to remove
Node* to_remove = head;
while(to_remove && to_remove->data != item)
  to_remove = to_remove->next;

// Do the removal if we found it
if(to_remove)
{
  // If it was at the head, advance the head to the next item
  if(to_remove == head)
    head = head->next;
  // If it was at the tail, advance the tail to the previous item
  if(to_remove == tail)
    tail = tail->previous;

  // Remove from the list
  if(to_remove->next)
    to_remove->next->previous = to_remove->previous;
  if(to_remove->previous)
    to_remove->previous->next = to_remove->next;

  // Free the removed node
  delete to_remove;
  count--;
  return true;
}

return false;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top