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
In general you are correct - the algorithm you posted takes an element itself as input though and not just its key:
Note that CHAINED-HASH-DELETE takes as input an element x and not its key k, so that we don't have to search for x first.
You have the element x - since it is a double linked list you have pointers to predecessor and successor, so you can fix those elements in O(1) - with a single linked list only the successor would be available, so you would have to search for the predecessor in O(n).
The textbook is wrong. The first member of a list has no usable "previous" pointer, so additional code is needed to find and unlink the element if it happens to be the first in the chain (typically 30 % of the elements are the head of their chain, if N=M, (when mapping N items into M slots; each slot having a separate chain.))
EDIT:
A better way then using a backlink, is to use a pointer to the link that points to us (typically the ->next link of the previous node in the list)
struct node {
struct node **pppar;
struct node *nxt;
...
}
deletion then becomes:
*(p->pppar) = p->nxt;
And a nice feature of this method is that it works equally well for the first node on the chain (whose pppar pointer points to some pointer that is not part of a node.
UPDATE 2011-11-11
Because people fail to see my point, I'll try to illustrate. As an example there is a hashtable table
(basically an array of pointers)
and a buch of nodes one
, two
, three
one of which has to be deleted.
struct node *table[123];
struct node *one, *two,*three;
/* Initial situation: the chain {one,two,three}
** is located at slot#31 of the array */
table[31] = one, one->next = two , two-next = three, three->next = NULL;
one->prev = NULL, two->prev = one, three->prev = two;
/* How to delete element one :*/
if (one->prev == NULL) {
table[31] = one->next;
}
else {
one->prev->next = one->next
}
if (one->next) {
one->next->prev = one->prev;
}
Now it is obvious that the obove code is O(1), but there is something nasty: it still needs array
, and the index 31
, so in most cases a node is "self contained", and a pointer to a node is sufficient to delete it from its chain, except when it happens to be the first node in its chain; additional information will then be needed to find table
and 31
.
Next, consider the equivalent structure with a pointer-to-pointer as a backlink.
struct node {
struct node *next;
struct node **ppp;
char payload[43];
};
struct node *table[123];
struct node *one, *two,*three;
/* Initial situation: the chain {one,two,three}
** is located at slot#31 of the array */
table[31] = one, one-next = two , two-next = three, three->next = NULL;
one->ppp = &table[31], two->ppp = &one->next, three->ppp = &two-next;
/* How to delete element one */
*(one->ppp) = one->next;
if (one->next) one->next->ppp = one->ppp;
Note: no special cases, and no need to know the parent table. (consider the case where there is more than one hashtable, but with the same nodetypes: the delete operation would still need to know from which table the node should be removed).
Often, in the {prev,next} scenario, the special cases are avoided by adding a dummy node at the start of the double linked list; But that needs to be allocated and initialised, too.