The idea of Cuckoo hashing is to resolve collisions by using two hash functions instead of only one.
In the basic variant of Cuckoo hashing, we use two hash tables $$T_1$$ and $$T_2$$ of equal size $$N$$, and we index each key $$k$$ with the hash functions $$H_1(k)=k \% N$$, respectively $$H_2(k)= \lfloor \frac{k}{N} \rfloor \% N$$.
Here are the main operations:
- Search: an element $$x$$ can exist in one of two locations: in $$T_1$$ at position $$H_1(x)$$ or in $$T_2$$ at position $$H_2(x)$$. Check both locations.
- Delete: we look at the two possible locations, and if the element is there, delete it.
- Insert: we put $$x$$ in $$T_1[H_1(x)]$$. If there was some element $$y$$ stored in that location, $$y$$ must be evicted (i.e. transfered to the second table) (thus the name "cuckoo" hashing). We put $$y$$ in its other valid location $$T_2[H_2(y)]$$. If that location is occupied by some element $$z$$, we have to evict $$z$$ and insert it in its other valid location $$T_1[H_1(z)]$$. We continue like this until we find an empty place and the process finishes, or until we give up because it starts to take too long (perhaps because we ran into a loop). If the latter happens, we conclude that insert failed, we stop.
Consider the following example:
Let $$N=11$$, $$H_1(k)=k \% 11$$ and $$H_2(k)= \lfloor \frac{k}{11} \rfloor \% 11$$.
Let the keys be the following, along with their hashing values:
\begin{array}{| c | c | c |}\hline
k & H_1(k) & H_2(k) \\ \hline
20 & 9 & 1 \\ \hline
50 & 6 & 4 \\ \hline
53 & 9 & 4 \\ \hline
75 & 9 & 6 \\ \hline
72 & 6 & 6 \\ \hline
\end{array}
Insertion of 20:
\begin{array}{|c |c|c|c|c|c|c|c|c|c|c|c|c|}\hline
index & 0 & 1&2&3&4&5&6&7&8&9&10 \\ \hline
T_1 & & &&&&&&&&20& \\ \hline
T_2 & & &&&&&&&&& \\ \hline
\end{array}
Insertion of 50:
\begin{array}{|c |c|c|c|c|c|c|c|c|c|c|c|c|}\hline
index & 0 & 1&2&3&4&5&6&7&8&9&10 \\ \hline
T_1 & & &&&&&50&&&20& \\ \hline
T_2 & & &&&&&&&&& \\ \hline
\end{array}
Insertion of 53:
\begin{array}{|c |c|c|c|c|c|c|c|c|c|c|c|c|}\hline
index & 0 & 1&2&3&4&5&6&7&8&9&10 \\ \hline
T_1 & & &&&&&50&&&53& \\ \hline
T_2 & & 20 &&&&&&&&& \\ \hline
\end{array}
Insertion of 75:
\begin{array}{|c |c|c|c|c|c|c|c|c|c|c|c|c|}\hline
index & 0 & 1&2&3&4&5&6&7&8&9&10 \\ \hline
T_1 & & &&&&&50&&&75& \\ \hline
T_2 & & 20 &&&53&&&&&& \\ \hline
\end{array}
Insertion of 72:
\begin{array}{|c |c|c|c|c|c|c|c|c|c|c|c|c|}\hline
index & 0 & 1&2&3&4&5&6&7&8&9&10 \\ \hline
T_1 & & &&&&&72&&&53& \\ \hline
T_2 & & 20 &&&50&&75&&&& \\ \hline
\end{array}
In the following, consider that all keys are $$\geq$$ 0.
- Write a function that checks whether an element $$x$$ belongs to such hash tables.
- Write a function that deletes an element $$x$$ from such hash tables.
- Write a function that inserts an element $$x$$ into such hash tables.
Difficulty level
Video recording
This exercise is mostly suitable for students
typedef struct
{
element T1[N];
element T2[N];
} Hash;
Hash Create()
{
Hash h;
int i;
for(i=0;i<N;i++)
{
h.T1[i]=-1;
h.T2[i]=-1;
}
return h;
}
int belong(Hash h, element e)
{
int v1, v2;
v1=hash1(e);
v2=hash2(e);
return( h.T1[v1]!=-1 && h.T1[v1]==e || h.T2[v2]!=-1 && h.T2[v2]==e);
}
int deleteh(Hash *h, element e)
{
int v1, v2;
v1=hash1(e);
v2=hash2(e);
if(h->T1[v1]!=-1 && h->T1[v1]==e)
h->T1[v1]=-1;
else
if(h->T2[v2]!=-1 && h->T2[v2]==e)
h->T2[v2]=-1;
else
return 0;
return 1;
}
int insert(Hash *h, element e)
{
int i=1;
int pass=1;
element el=e,temp;
if(belong(*h,e))
return 0;
while(i<2*N)
{
if(pass==1)
{
if(h->T1[hash1(el)]==-1)
{
h->T1[hash1(el)]=el;
return 1;
}
temp=h->T1[hash1(el)];
h->T1[hash1(el)]=el;
el=temp;
pass=2;
}
else
{
if(h->T2[hash2(el)]==-1)
{
h->T2[hash2(el)]=el;
return 1;
}
temp=h->T2[hash2(el)];
h->T2[hash2(el)]=el;
el=temp;
pass=1;
}
i++;
}
return 0;
}
Back to the list of exercises
Looking for a more challenging exercise, try this one !!
Binary search trees in levels