50
votes

In an interview today I got asked the question.

Apart from answering reversing the list and both forward and backward traversal there was something "fundamental" in it that the interviewer kept stressing. I gave up and of course after interview did a bit of research. It seems that insertion and deletion are more efficient in doubly linked list than singly linked list. I am not quite sure how it can be more efficient for a doubly linked list since it is obvious that more references are required to change. Can anybody explain the secret behind? I honestly did a quite a bit of research and failed to understand with my main trouble being the fact that a O(n) searching is still needed for the double linked list.

7

7 Answers

48
votes

Insertion is clearly less work in a singly-linked list, as long as you are content to always insert at the head or after some known element. (That is, you cannot insert before a known element, but see below.)

Deletion, on the other hand, is trickier because you need to know the element before the element to be deleted.

One way of doing this is to make the delete API work with the predecessor of the element to be deleted. This mirrors the insert API, which takes the element which will be the predecessor of the new element, but it's not very convenient and it's hard to document. It's usually possible, though. Generally speaking, you arrive at an element in a list by traversing the list.

Of course, you could just search the list from the beginning to find the element to be deleted, so that you know what its predecessor was. That assumes that the delete API includes the head of the list, which is also inconvenient. Also, the search is stupidly slow.

The way that hardly anyone uses, but which is actually pretty effective, is to define a singly-linked list iterator to be the pointer to the element preceding the current target of the iterator. This is simple, only one indirection slower than using a pointer directly to the element, and makes both insertion and deletion fast. The downside is that deleting an element may invalidate other iterators to list elements, which is annoying. (It doesn't invalidate the iterator to the element being deleted, which is nice for traversals which delete some elements, but that's not much compensation.)

If deletion is not important, perhaps because the datastructures are immutable, singly-linked lists offer another really useful property: they allow structure-sharing. A singly-linked list can happily be the tail of multiple heads, something which is impossible for a doubly-linked list. For this reason, singly-linked lists have traditionally been the simple datastructure of choice for functional languages.

26
votes

Here is some code that made it clearer to me... Having:

class Node{
      Node next;
      Node prev;
}

DELETE a node in a SINGLE LINKED LIST -O(n)-

You don't know which is the preceeding node so you have to traverse the list until you find it:

deleteNode(Node node){
    prevNode = tmpNode;
    tmpNode = prevNode.next;

    while (tmpNode != null) {
        if (tmpNode == node) {
            prevNode.next = tmpNode.next;
        }
        prevNode = tmpNode;
        tmpNode = prevNode.next;
    }
}

DELETE a node in a DOUBLE LINKED LIST -O(1)-

You can simply update the links like this:

deleteNode(Node node){
    node.prev.next = node.next;
    node.next.prev = node.prev;
}
11
votes

Here are my thoughts on Doubly-Linked List:

  • You have ready access\insert on both ends.

  • it can work as a Queue and a Stack at the same time.

  • Node deletion requires no additional pointers.

  • You can apply Hill-Climb traversal since you already have access on both ends.

  • If you are storing Numerical values, and your list is sorted, you can keep a pointer/variable for median, then Search operation can be highly optimal using Statistical approach.

6
votes

If you are going to delete an element in a linked list, you will need to link the previous element to the next element. With a doubly linked list you have ready access to both elements because you have links to both of them.

This assumes that you already have a pointer to the element you need to delete and there is no searching involved.

2
votes

'Apart from answering reversing the list and both forward and backward traversal there was something "fundamental"'.

Nobody seem to have mentioned: in a doubly linked list it is possible to reinsert a deleted element just by having a pointer to the deleted element. See Knuth's Dancing Links paper. I think that's pretty fundamental.

0
votes
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 ;
0
votes

Singly Linked List vs Doubly Linked List vs Dynamic Arrays:

When comparing the three main data structures, Doubly Linked Lists are most efficient in all major tasks and operations when looking at time complexity. For Doubly Linked Lists, it operates at constant time for all operations except only access by index, where it operated at linear time (n) as it needs to iterate through each node to get to the required index. When it comes to Insert, Remove, First, Last, Concatenation and Count, Doubly Linked list operates at constant time where Dynamic Arrays operate at linear time (n).

In terms of space complexity, Dynamic Arrays stores only elements therefore constant time complexity, singly linked lists stores the successor of each element therefore linear space complexity (n), and worst of all doubly linked list stores the predecessor and successor of each element and therefore also linear space complexity but (2*n).

Unless you have extremely limited resources / space then perhaps either Dynamic arrays or Singly linked lists are better, however, nowadays, space and resources are more and more abundant and so doubly linked lists are far better with the cost of more space.