24

It is said that the complexity of the LinkedList remove and the add operation is of O(1). and in case of ArrayList it is of O(n).

Calculation for ArrayList of size "M" : if i want to remove the element at Nth position then i can directly go to the Nth position using index in one go (i don't have to traverse till Nth index) and then i can remove the element, till this point the complexity is O(1) then i will have to shift the rest of the elements(M-N shifts) so my complexity will be linear i.e. O(M-N+1). and hence deletion or insertion at the last will give me the best performence( as N ~ M) and deletion or insertion at the start will be worst (as N ~ 1).

Now the LisnkedList of size "M" : as we can not directly reach the Nth element in the LinkedList, to access the Nth element we have to traverse N elements, so the search in the LinkedList is costlier then the ArrayList...but Remove and the add operations are said to be of O(1) in case of LinkedList as, in LinkedList the Shift is not involved, but there is traverse operation involved rigth ? so the complexity should be of order O(n) where Worst performence will be at the tail node and best performence will be at the head node.

Could anyone please explain me why don't we consider the traverse cost while calculating the complexity of LinkedList remove operation ?

So i wants to understand how it works in java.util package. and if i want to implemet the same in C or C++ how would i achieve the O(1) for random deletion and insertion in LinkedList ?

Aditya Agarwal
  • 693
  • 1
  • 10
  • 17
  • Possible duplicate of [How is LinkedList's add(int, E) of O(1) complexity?](http://stackoverflow.com/questions/15732334/how-is-linkedlists-addint-e-of-o1-complexity) – jdigital Mar 17 '17 at 05:07
  • The O(1) is for insertion or deletion having found the location in the doubly-linked list at which to perform the operation. If that location is neither the head nor tail of the list, and you haven't pointed to it explicitly in some other way, then you have to find it and that part is O(n). – Lew Bloch Mar 17 '17 at 05:10

6 Answers6

55

Remove and the add operations are said to be of O(1) in case of LinkedList as, in LinkedList the shift is not involved, but there is traverse operation involved right?

Adding to either end of a linked list does not require a traversal, as long as you keep a reference to both ends of the list. This is what Java does for its add and addFirst/addLast methods.

Same goes for parameterless remove and removeFirst/removeLast methods - they operate on list ends.

remove(int) and remove(Object) operations, on the other hand, are not O(1). They requires traversal, so you correctly identified their costs as O(n).

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • "Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null)." This line is from oracle java docs https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html#remove(java.lang.Object). If it is doubly-linked list implementation, then remove(Object) method should take O(1) right? – Sam Feb 04 '19 at 04:21
  • @Sam `Remove(Object)` still requires a scan, so it's O(n). See implementation on line 353 [here](http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/java/util/LinkedList.java). – Sergey Kalinichenko Feb 04 '19 at 10:51
  • @dasblinkenlight in a doubly-linked list, every node has access to the previous and next node, so when remove is called, the node can connect the previous with it's next in O(1). – RagHaven Apr 14 '19 at 01:51
  • 1
    @AndroidDev93 In `remove(n)` it's not the removal that's O(n), it's getting to node `n` that is. – Sergey Kalinichenko Apr 14 '19 at 07:58
  • 1
    @dasblinkenlight if you already have access to the node that needs to be removed, why do you need to spend O(n) to get to the node? There is a version of the remove function that takes the Node Object as input. When you have Object, you do not need to spend O(n) to get to the Object. So, the remove function will be O(1). – RagHaven Apr 15 '19 at 08:32
  • 3
    You don't have access to node because nodes are kept private to LinkedList and are not given to outside world. If LinkedList.add method returned a Node to you, then yes, you could use that node to delete element in O(1). But that's not the case. LinkedList.remove method is given the object, not the node, and have to traverse the list to find the node containing that object – Kirill Gamazkov Nov 27 '19 at 10:06
  • You could iterate a LinkedList using a ListIterator to get to the required element at O(n) and then just remove that element at a later point in time using the List Iterator's remove() method at O(1), as the ListIterator would then already have a reference to the required Node. This kind of separates the two operations but of course would still require that prior O(n) traversal – Zippy Dec 13 '20 at 01:08
12

The complexity of removing is considered that you already have the pointer to the right position of the element you want to remove...

Is not considered the cost you took for finding it

Information on this topic is now available on Wikipedia at: Search data structure

    +----------------------+----------+------------+----------+--------------+
    |                      |  Insert  |   Delete   |  Search  | Space Usage  |
    +----------------------+----------+------------+----------+--------------+
    | Unsorted array       | O(1)     | O(1)       | O(n)     | O(n)         |
    | Value-indexed array  | O(1)     | O(1)       | O(1)     | O(n)         |
    | Sorted array         | O(n)     | O(n)       | O(log n) | O(n)         |
    | Unsorted linked list | O(1)*    | O(1)*      | O(n)     | O(n)         |
    | Sorted linked list   | O(n)*    | O(1)*      | O(n)     | O(n)         |
    | Balanced binary tree | O(log n) | O(log n)   | O(log n) | O(n)         |
    | Heap                 | O(log n) | O(log n)** | O(n)     | O(n)         |
    | Hash table           | O(1)     | O(1)       | O(1)     | O(n)         |
    +----------------------+----------+------------+----------+--------------+

 * The cost to add or delete an element into a known location in the list (i.e. if you have an iterator to the location) is O(1). If you don't know the location, then you need to traverse the list to the location of deletion/insertion, which takes O(n) time. 
** The deletion cost is O(log n) for the minimum or maximum, O(n) for an arbitrary element.
Rafael Lima
  • 3,079
  • 3
  • 41
  • 105
1

Yes, you are corrrect if you consider two operations (indexing and inserting) in one go. It is not true in this case because when you are inserting a node in the middle of a linked list, the assumption taken is that you are already at the address where you have to insert the node.

The time complexity of accessing the node is O(n) whereas only inserting a node is O(1).

Insertion at the head requires you to add the element and update the head pointer.

newnode->next = head;
head = newnode;

Insertion at the tail requires you to keep a pointer to the tail element, add the element at the tail and update the tail pointer.

tail->next = newnode;
tail = newnode;

Deleting the head element requires updating the head and deleting the previously head element.

temp = head;
head = head->next;
delete temp; /* or free(temp); */

All the above are trivial operations and don’t depend upon the number of elements in linked list. Hence, they are O(1)

Deleting the tail element would, however, be a O(n) operation because even though you might have a tail pointer, you would still need the penultimate node that would be setup as the new tail node ( by updating the tail pointer and setting the node’s next member to NULL). For this, you need to traverse through the whole linked list.

penultimate_el = find_penultimate_el(head); /* this is O(n) operation */
delete tail; /* or free(tail) */
tail = penultimate_el;
tail->next = NULL;
Ankit Joshi
  • 183
  • 1
  • 3
  • 9
1

ArrayList provides resizable-array and stores "references" or "pointers" to actual storage. This array of references has to be recreated if the array is expanded beyond its allocated size. In other words, inserting a node at the beginning would require either all the existing elements to be moved up one, or to re-allocate the whole list if it's beyond it's allocated size. That's why insertion is O(n).

A LinkedList consists of a chain of nodes; each node is separated allocated. And so while inserting, it's not necessary to traverse all the nodes. And that's why it has the complexity O(1). However, if you're inserting at end and you have reference of only first node, then you may have to traverse the entire list and thus complexity in this case will be O(n).

EDIT

If you look at the source code of java.util.LinkedList you can find that LinkedList always keeps the track of last element

Following are some code snippets from actual java.util.LinkedList class.

package java.util;

...

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;

    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;


    ...
    ...


    /**
     * Appends the specified element to the end of this list.
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }



    ...
    ...


    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }


    ...
    ...

}

See particularly the linkLast() method. It doesn't traverse the entire list. It just inserts the element at the end of the list and that's why time complexity is O(1).

Community
  • 1
  • 1
Raman Sahasi
  • 30,180
  • 9
  • 58
  • 71
  • Do when i want to insert an element in linkedlist does it already know the address of the node after which it is going to be inserted ? does linkedlist stores the address of each element somewhere ? Please correct me if i am wrong but i think that it only knows the address of the head node and from head node it traverse to the Nth element ? and in that case the complexity to add at the Nth Position in LikedList will be "N" ?? – Aditya Agarwal Mar 17 '17 at 05:38
  • @AdityaAgarwal yes you're right. But the fact is the `LinkedList` which comes with `java.util` package keeps the track of last element. And so while inserting, it doesn't have to traverse the entire list. See the edited answer. – Raman Sahasi Mar 17 '17 at 06:05
  • so that means inserting element at the head or tail will be of O(1) but adding the element at the middle at Nth position the complexity will be O(N) as Java.util package have the address of the head an the last(tail) node ? – Aditya Agarwal Mar 17 '17 at 14:10
  • @AdityaAgarwal Yes. You can say that. – Raman Sahasi Mar 17 '17 at 17:14
  • Thanks for the response. Do you have any reference to support your last answer ? Appreciate your help. – Aditya Agarwal Mar 17 '17 at 17:17
  • @AdityaAgarwal Please visit this answer on StackOverflow. [Why is inserting in the middle of a linked list O(1)?](http://stackoverflow.com/a/840655/2815219) – Raman Sahasi Mar 17 '17 at 23:03
  • @AdityaAgarwal You're welcome. Please don't forget to mark the answer accepted which helped most in solving the problem. See also [How does accepting an answer work?](https://meta.stackexchange.com/q/5234/336295) – Raman Sahasi Mar 18 '17 at 16:51
0

In array if we see the implementation from C language point of view then it will give us a clear understanding. While we can add and remove elements at constant time in array i.e we don't need to traverse through the entire array Eg: If the array is [1,2,3,4] the 5 can be added directly at Arr[arr.length] postion , similarly we can remove element at constant time ,position to be deleted is Arr[arr.length-1], but let us see a scenario when the array size you have declared is 4 and we want add one more elements then we can clearly see that there is no space for the elements to be added, therefore what we need to do is to make a new array of size lets say double of previous array i.e size 8 , then all the elements of previous array has to be copied here and the new elements is added at 5th position i.e Arr[arr.length] , therefore the insertion time complexity is O(n) as it is directly proportional to number of elements that previous array has.

Now coming to linked list it doesn't have fixed size(it is dynamically allocated at heap memory) declared we can track the first and last position by head and tails pointer therefore irrespective of linkedlist size we need to just change the pointer of head and tail, making time complexity to O(1) ,for adding at last make a new node, change the link part of current tail to this new node address and make this new node as tail.For inserting at first we need to make new node , set the link part of this node as current head address and atlast make this new node as head thus adding a element at 1st position just by altering the one node therefore time complexity will be O(1).

Kunal Hazard
  • 141
  • 1
  • 5
0

All other answers seems to be correct. I just have one more point to add here. I think in doubly linked list the time complexity for removing any node will be O(1) if you have the reference of that node. Since it is doubly linked list and you have the reference of the node in hand, you can directly update pointers of previous node and thus removing the node with reference will cost you just O(1) time. Because of doubly linked list property, you do not need to traverse and that's why time complexity of removing a node using reference will be O(1).

You can read more about this here: Time Complexity of Doubly Linked List Element Removal?

Sukhbir
  • 553
  • 8
  • 23