On CLRS\'s textbook \"Introduction to Algorithm\", there\'s such paragraph on pg. 258.
We can delete an element in O(1) time if the lists are doubly linked. (Note th
It seems to me that the hash table part of this is mostly a red herring. The real question is: "can we delete the current element from a linked list in constant time, and if so how?"
The answer is: it's a little tricky, but in effect yes, we can -- at least usually. We do not (normally) have to traverse the entire linked list to find the previous element. Instead, we can swap the data between the current element and the next element, then delete the next element.
The one exception to this is when/if we need/want to delete the last item in the list. In this case, there is no next element to swap with. If you really have to do that, there's no real way to avoid finding the previous element. There are, however, ways that will generally work to avoid that -- one is to terminate the list with a sentinel instead of a null pointer. In this case, since we never delete the node with the sentinel value, we never have to deal with deleting the last item in the list. That leaves us with relatively simple code, something like this:
template <class key, class data>
struct node {
key k;
data d;
node *next;
};
void delete_node(node *item) {
node *temp = item->next;
swap(item->key, temp->key);
swap(item->data, temp->data);
item ->next = temp->next;
delete temp;
}
While going through the textbook, I also got confused on the same topic(whether "x" is a pointer to an element or the element itself) and then eventually landed upon this question. But after going through the above discussion and referring textbook again, I think in the book "x" is implicitly assumed to be a "node" and its possible attributes are "key", "next".
Some lines form the textbook..
1)CHAINED-HASH-INSERT(T,x) insert x at the head of list T[h(x.key)]
2)If the lists were only singly linked, then to delete element x, we would first have to find x in the list T[h(x.key)] so that we could update the next attribute of x’s predecessor.
Hence we can assume that the pointer to the element is given and I think Fezvez has given a good explanation for the asked question.
The problem presented here is : consider you have are looking at a particular element of a hashtable. How costly is it to delete it?
Suppose you have a simple linked list :
v ----> w ----> x ----> y ----> z
|
you're here
Now if you remove x
, you need to connect w
to y
to keep your list linked. You need to access w
and tell it to point to y
(you want to have w ----> y
). But you can't access w
from x
because it's simply linked! Thus you have to go through all your list to find w
in O(n) operations, and then tell it to link to y
. That's bad.
Then, suppose you're doubly-linked :
v <---> w <---> x <---> y <---> z
|
you're here
Cool, you can access w and y from here, so you can connect the two (w <---> y
) in O(1) operation!
suppose you want to delete an element x , by using doubly link list you can easily connect the previous element of x to next element of x. so no need to go through all the list and it will be in O(1).
Find(x)
is, in general, O(1) for a chained hash table -- it is immaterial whether or not you use singly linked lists or doubly linked lists. They are identical in performance.
If, after having run Find(x)
, you decide that you'd like to delete the object returned, you will find that, internally, a hash table might have to search for your object again. It's still usually going to be O(1) and not a big deal, but you find that you delete an awful lot, you can do a little better. Instead of returning a user's element directly, return a pointer to the underlying hash node. You can then take advantage of some internal structures. So if in this case, you chose a doubly linked list as the way to express your chaining, then during the delete process, there is no need to recompute the hash and search the collection again -- you can omit this step. You have enough information to perform a delete right from where you are sitting. Additional care must be taken if the node you are submitting is the head node, so an integer might be used to mark the location of your node in the original array if it is the head of a linked list.
The trade-off is the guaranteed space taken by the extra pointer versus a possible faster delete (and slightly more complicated code). With modern desktops, space is usually very cheap, so this might be a reasonable trade-off.
Coding point of view:
one can use unordered_map
in c++ to implement this.
unordered_map<value,node*>mp;
Where node*
is a pointer to a structure storing the key, left and right pointers!
How to use:
If you have a value v
and you want to delete that node just do:
Access that nodes value like mp[v]
.
Now just make its left pointer point to the node on its right.
And voila, you are done.
(Just to remind, in C++ unordered_map
takes an average O(1) to access a specific value stored.)