Time Complexity of Doubly Linked List Element Removal?
A lot of what I'm reading says that removing an internal element in a doubly linked list (DLL) is O(1)
; but why is this the case?
I understand why it's 开发者_高级运维O(n)
for SLLs; traverse the list O(n)
and remove O(1)
but don't you still need to traverse the list in a DLL to find the element?
For a doubly linked list, it's constant time to remove an element once you know where it is.
For a singly linked list, it's constant time to remove an element once you know where it and its predecessor are.
Since that link you point to shows a singly linked list removal as O(n)
and a doubly linked one as O(1)
, it's certain that's once you already know where the element is that you want to remove, but not anything else.
In that case, for a doubly linked list, you can just use the prev
and next
pointers to remove it, giving you O(1)
. Ignoring the edge cases where you're at the head or tail, that means something like:
corpse->prev->next = corpse->next
corpse->next->prev = corpse->prev
free (corpse)
However, in a singly linked list where you only know the node you want deleted, you can't use corpse->prev
to get the one preceding it because there is no prev
link.
You have to instead find the previous item by traversing the list from the head, looking for one which has a next
of the element you want to remove. That will take O(n)
, after which it's once again O(1)
for the actual removal, such as (again, ignoring the edge cases for simplicity):
lefty = head
while lefty->next != corpse:
lefty = lefty-> next
lefty->next = corpse->next
free (corpse)
That's why the two complexities are different in that article.
As an aside, there are optimisations in a singly-linked list which can make the deletion O(n)
(the deletion being effectively O(1) once you've found the item you want to delete, and the previous item). In code terms, that goes something like:
# Delete a node, returns true if found, otherwise false.
def deleteItem(key):
# Special cases (empty list and deleting head).
if head == null: return false
if head.data == key:
curr = head
head = head.next
free curr
return true
# Search non-head part of list (so prev always exists).
prev = head
curr = head.next
while curr != null:
if curr.data == key:
# Found it so delete (using prev).
prev.next = curr.next
free curr
return true
# Advance to next item.
prev = curr
curr = curr.next
# Not found, so fail.
return false
As it's stated where your link points to:
The cost for changing an internal element is based on already having a pointer to it, if you need to find the element first, the cost for retrieving the element is also taken.
So, for both DLL and SLL linear search is O(n), and removal via pointer is O(1).
The complexity of removal in DLL is O(1)
.
It can also be O(1)
in SLL if provided pointer to preceding element and not to the element itself.
This complexity is assuming you know where the element is.
I.e. the operation signature is akin to remove_element(list* l, link* e)
Searching for the element is O(n)
in both cases.
@Matuku: You are correct.
I humbly disagree with most answers here trying to justify how delete operation for DLL is O(1). It's not.
Let me explain.
Why are we considering the scenario that we 'would' have the pointer to the node that is being deleted? LinkedLists (Singly/Doubly) are traversed linearly, that's their definition. They have pointers only to the head/tail. How can we suddenly have a pointer to some node in between? That defeats the purpose of this data structure. And going by that assumption, if I have a DLL list of say 1 million nodes, then do I also have to maintain 1 million pointers (let's call them access pointers) pointing to each of those nodes so that I can delete them in O(1)? So how would I store those 1 millions access pointers? And how do I know which access pointer points to the correct data/node that I want to delete?
Can we have a real world example where we 'have' the pointer to the data that has to be deleted 100% of the time?
And if you know the exact location/pointer/reference of/to the node to be deleted, why to even use a LinkedList? Just use array! That's what arrays are for - direct access to what you want!
By assuming that you have direct access to any node you want in DLL is going against the whole idea of LinkedList as a conceptual Data Structure. So I agree with OP, he's correct. I will stick with this - Doubly LinkedLists cannot have O(1) for deleting any node. You still need to start either from head or tail, which brings it down to O(n).
" If " we have the pointer to the node to be deleted say X, then of course it's O(1) because we have pointers to the next and prev node we can delete X. But that big if is imaginary, not real.
We cannot play with the definition of the sacred Data Structure called LinkedLists for some weird assumptions we may have from time to time.
精彩评论