views:

2274

answers:

10

Hi, as far as I can see, you can do:

  1. Find the node to remove.
  2. node.previous.next = node.next
  3. node.next.previous = node.previous
  4. node.previous = null
  5. node.next = null
  6. Dispose of node if you're in a non-GC environment

If your list is a double linked.

But how do you do it with a single linked list? I have tried a lot of things, with no avail :( I simply get it to remove a specific index instead or it does nothing at all

+10  A: 

Start at the beginning of the list. Maintain a reference to the current item (currentItem) and the previous item (previousItem). Linearly search for the item that you want to remove always walking with previousItem = currentItem, currentItem = currentItem.Next. If the item that you want to remove is the head of the list, reassign the head of the list to currentItem.Next. Otherwise, set previousItem.Next = currentItem.Next. If necessary (as you say, in a non-GC environment) dispose of currentItem.

Basically you are using previousItem to mimic the behavior of a currentItem.Previous in the case of a doubly-linked list.

Edit: This is a correct implementation of Delete:

public void Delete(int rangeStart, int rangeEnd) {
    Node previousNode = null, currentNode = Head;
    while (currentNode != null) {
        if (currentNode.Data >= rangeStart && currentNode.Data <= rangeEnd) {
            if (previousNode == null) {
                Initial = currentNode.Next;
            }
            else {
                previousNode.Next = currentNode.Next;
            }
        }
        else {
            previousNode = currentNode;
        }
        currentNode = currentNode.Next;
    }
}
Jason
That is clever! I had not thought of mimicing!Although, I does not seem to work, if the item is the very first item in my list?
CasperT
@CasperT: If the item is the very first in the list you merely reassign the head of the list to `currentItem.Next` and, if necessary, dispose of `currentItem`. In this case the `previousItem` reference is not needed.
Jason
I think I am a little off the track. I have updated my question :)
CasperT
Oh and also if the item is in the end of my list - won't work either :)
CasperT
There. Fixed it :)
CasperT
@CasperT: What you have provided will not work. Consider 1->2->3->4->5 and calling `Delete(1, 2)`. You will end with the list `2->3->4->5` because the `Initial` node will not be correctly set. This is because you only modify `Initial` when `previousNode` is `null`. After you've walked over the initial item in the list you will have `Initial` pointing to the node containing 2, `previousNode` pointing to the node containing 1 and `currentNode` pointing to the node containing 2. Thus the next time through the loop `previousNode` will not be `null` so `Initial` will not change. I'll provide a fix.
Jason
Your right. I didn't test it enough at all. Thanks
CasperT
And to bug you some more :DDoesn't it seem like an extreme amount of code, to remove a value?The remove version double linked List has, is much shorter, according to reflector: http://pastebin.com/m6acb7ca1
CasperT
Note that this is an O(n) algorithm, as opposed to O(1) as it is with a doubly linked list. If your decision to use a linked list related to efficiency, this is an issue that is important to take note of. If you already know what the previous node is for some other reason, then of course you can do better.
Brian
@CasperT: Note first that the code that you linked to is removing a specific node in a linked list and not removing, say, all nodes having a given value, or all nodes having a value in a certain range. That is, comparing the above code to the code that you linked to (pun intended) isn't apples-to-apples. Second, I don't consider this to be an extreme amount of code. The algorithm is quite straightforward and the code does what it has to do to get the job done; I don't see a simpler approach nor do I see a need to try to find one.
Jason
@Brian: I don't think that you are making a fair comparison. The algorithm to remove all items in a certain range in a doubly-linked list will also be `O(n)` as you still have to walk through the list to find the items to remove. This is inherently `O(n)`.
Jason
@CasperT: With a doubly linked list, it is MUCH simpler, because the previous node is already known. Most of the code in that implementation could be replaced by a call to currentNode.Prev...but that does not exist in a singly-linked list.
Brian
@Jason: The algorithm to remove 1 item in a doubly-linked list is O(1), but it's O(n) in a singly linked list because it takes O(n) time to determine where the previous node is in the node. Also, the algorithm to remove all items in a particular range k of a doubly linked list, given a start and end node, is still O(1)...though there will be O(k) garbage collection-related operations eventually.
Brian
+5  A: 

You keep track of the last node while you try to find the "current node".

Then you can wire up the previouse.next to current.next and you are done

Heiko Hatzfeld
It doesn't seem to work if the desired item(s) is either in the end or at the start of the list
CasperT
You pseudocode didnt handle that case either ;)
Heiko Hatzfeld
Touche :P and wow it takes a lot of code to handle the rest
CasperT
+2  A: 

The following code uses recursion to keep track of previous node: Source: http://www.cs.bu.edu/teaching/c/linked-list/delete/

nodeT *ListDelete(nodeT *currP, elementT value)
{
  /* See if we are at end of list. */
  if (currP == NULL)
    return NULL;

  /*
   * Check to see if current node is one
   * to be deleted.
   */
  if (currP->element == value) {
    nodeT *tempNextP;

    /* Save the next pointer in the node. */
    tempNextP = currP->next;

    /* Deallocate the node. */
    free(currP);

    /*
     * Return the NEW pointer to where we
     * were called from.  I.e., the pointer
     * the previous call will use to "skip
     * over" the removed node.
     */
    return tempNextP;
  }

  /*
   * Check the rest of the list, fixing the next
   * pointer in case the next node is the one
   * removed.
   */
  currP->next = ListDelete(currP->next, value);


  /*
   * Return the pointer to where we were called
   * from.  Since we did not remove this node it
   * will be the same.
   */
  return currP;
}
alex
+2  A: 

Well, you could just use LinkedList<T> and Remove; but manually:

  • iterate forwards until you find the node you want to remove keeping the previous node available in a variable at each point
  • set prev.next = node.next
  • go home
Marc Gravell
Should check prev for null!Isnt it?
Aviator
calling LinkedList<T> "language-agnostic" would be a bit of a stretch ;-)
HerdplattenToni
@HerdplattenToni - check the edit history; it was C# at one point!
Marc Gravell
@Marc Gravell: I made the edit because his first version of the post seemed to be looking for the idea and not code in a specific language (as indicated by his walkthrough on how to do it for a doubly-linked list). Now that he has added code, I have added the tag back. It does not appear that he is using `LinkedList<T>` however.
Jason
+1  A: 

keep remebering the last node you been too.

//PSEUDO CODE

Node prevnode = null;
foreach (node n in nodes)
{
    if (n.name.equals(name))
    {
     if (prevnode != null)
     {
      prevnode.next = n.next;
     }
     remove n;
     break;
    }

    prevnode = n;
}
PoweRoy
I am not sure how you could implement a foreach :)
CasperT
therefore a pseudo code tag. Just like the remove
PoweRoy
A: 

This is the primary weakness of the singly-linked list. You'll either need to have a reference to the previous node, or scan through the list from the beginning.

Tor Haugen
A: 

In THEORY, you could do this to remove any random node from a single link list:

void RemoveNode(Node toRemove)
{
    toRemove.PointerToSomeValue = toRemove.NextNode.PointerToSomeValue ;
    toRemove.NextNode = toRemove.NextNode.NextNode ;

}

But, you need to be carefull about concurency issues. (ie. somebody else has a link to your node assuming it still caries the old value (an ABA problem) ), and you need to have a "marker" (empty) node at the end of the list, which you must never attempt to delete.(because of the toRemove.next.next)

Edit: obviously PointerToSomeValue is whatever data you want to keep in your list. And you're not actually deleting the node, you're basically removing the next node in the list, oh,well.. you get the ideea

Radu094
A: 

Almost ironic you should just ask this. I'm trying to brush up on my singly linked lists too. I just wrote this function in C++ that seems to work:

void pop_back() {
    if(head == NULL) {
        return;
    }

    if(head->next == NULL) {
        delete head;
        head = NULL;
        return;
    }

    Node *iter = head;
    while(iter->next->next != NULL) {
        iter = iter->next;
    }
    delete iter->next;
    iter->next = NULL;
}

For popping the last element, but you can modify it slightly to work in the middle, I'm sure. Idea is pretty much the same in C#.

Mark
Hi. I wrote a pop() earlier today.http://pastebin.com/m7bd8ca12It seems to work and is much shorter :) it does need a null validate though
CasperT
Well, that pops the first element. Mine pops the last... not quite as easy.
Mark
Oh, okay, I am treating my list as a stack :) I did not notice that
CasperT
A: 

You may find the comprehensive implementation of Singly Linked List with all the functions such Add, Remove, InsertAt etc., here. http://technicalypto.blogspot.com/2010/01/linked-lists.html Note: This has a working Java code + Test classes so that you would not miss out on anything that a singly linked list is made of,

Bragaadeesh
A: 

struct node_t {

int data;
struct node_t *next;

}

void delete_node( struct node_t *random) {

struct node_t *temp;

/* Save the ptr to the next node */
temp = random->next;

/* Copy stuff from the next node to this node */
random->data = random->next->data;
random->next = random->next->next;

/* Delete the next node */
free (temp);

return;

}

This should work on most Operating Systems in my opinion.

Abhishek Hariharan