>> typedef struct Dictionary* DictionaryRef;
I don't like hiding pointers with typedef. It's confusing to readers and maintenance programmers. You would be better off just using Dictionary *.
>> void delete(DictionaryRef D, int k);
Deletion in a hash table isn't trivial unless you use separate chaining. However, your comment suggests that you're supposed to use some variation of linear probing, so I'll assume that's what you want and show you deletion using a DELETED status.
lookup is just taking a hash of the key and checking that location to see if it's empty. If not, you go to the next location and do the same test. Repeat until you get back to where you started, in which case the key isn't found:
int lookup(Dictionary *dict, int key)
{
unsigned start = hash(key);
if (dict->dict[start].status == FULL && key == dict->dict[start].key)
return dict->dict[start].value;
else {
/* Linear probe until found or back at start */
unsigned probe = start == DICT_SIZE - 1 ? 0 : start + 1;
while (probe != start) {
if (dict->dict[probe].status == FULL && key == dict->dict[probe].key)
return dict->dict[probe].value;
if (++probe == DICT_SIZE) probe = 0;
}
return UNDEF;
}
}
The tricky part is making sure that you don't go around and around in an infinite loop. Deletion is just a variant of lookup where instead of returning the value, you mark the status as DELETED so that it won't effect future insertions, but still won't be treated as an existing item:
void delete(Dictionary *dict, int key)
{
unsigned start = hash(key);
if (dict->dict[start].status == FULL && key == dict->dict[start].key) {
dict->dict[start].status = DELETED;
--dict->size;
} else {
/* Linear probe until found or back at start */
unsigned probe = start == DICT_SIZE - 1 ? 0 : start + 1;
while (probe != start) {
if (dict->dict[probe].status == FULL && key == dict->dict[probe].key) {
dict->dict[probe].status = DELETED;
--dict->size;
}
if (++probe == DICT_SIZE) probe = 0;
}
}
}
Insertion is, oddly enough, yet another variant of lookup, where if the item doesn't exist in the dictionary, the first empty location is filled using the hashed key as a starting point:
void insert(Dictionary *dict, int key, int value)
{
if (lookup(dict, key) == UNDEF) {
unsigned start = hash(key);
unsigned probe = start;
if (dict->dict[start].status != EMPTY) {
while (++probe != start) {
if (probe == DICT_SIZE) probe = 0;
if (dict->dict[probe].status == EMPTY)
break;
}
if (probe == start)
return;
}
dict->dict[probe].key = key;
dict->dict[probe].value = value;
dict->dict[probe].status = FULL;
++dict->size;
}
}
Those functions are the heart of a hash table. Once they're implemented, the rest of the interface just falls into place. Of course, you also need to come up with a hash function, but that's another topic entirely. ;) After all that, remember that any form of linear probing is a pain in the butt to implement correctly. In fact, I wouldn't be surprised if the code I gave you had bugs because I wrote it in a hurry and didn't test it thoroughly. The same can't be said about separate chaining, which is much easier to get right the first time.