3
$\begingroup$

To prevent collisions, hash tables with open addressing use a methodology to chain the contents. Why can't we use another hash table allocated to each slot of the primary hash table?

$\endgroup$
2

2 Answers 2

8
$\begingroup$

The method you propose is, as far as I know, the historically first one for "perfect" hashing in linear space. In perfect hashing, lookup takes $O(1)$ time in the worst-case. (Recall that in most simple hash tables, lookup takes $O(1)$ time only in expectation.)

The idea is to use chaining (rather than open addressing), but make each chain a hash table of size $\Omega(m^2)$ where $m$ is the number of items in the bucket.

This is sometimes called "FKS", after the initials of the inventors. Here are some freely available resources:

$\endgroup$
4
$\begingroup$

The short answer is that this is, more or less, equivalent to having one hash table. Let's say you're hashing $n$ items into $m$ slots, and each slot has its own hash table of size $c$. You propose first hashing into one of the $m$ slots using some function $h$, then hashing again into one of the $c$ slots using some function $g$. This is more or less equivalent to hashing to any of the $cm$ slots initially, with a new hash function that combines your initial two hash functions. In other words, instead of taking $h(x)$ for which of the $m$ slots to hash it to and then $g(x)$ to find the appropriate slot in the subtable, you can take $f(x) = h(x)m + g(x)$ and get the same answer with a new hash function $f$.

Something else to consider is what you do if there are collisions in the second hash. You need...some sort of methodology to chain the contents! So you're back where you started with linear probing/chaining/etc, and all you've done is increase your hash table size from $m$ to $cm$.

$\endgroup$
4
  • 1
    $\begingroup$ Dynamically allocate inner hash tables, and then nested hashes are different than your suggestion. Certainly it's still true that eventually you need to break the hash inside hash recursion. $\endgroup$
    – rrenaud
    Oct 28, 2012 at 16:53
  • $\begingroup$ @rrenaud In that case, why not dynamically allocate the outer one? $\endgroup$
    – Raphael
    Nov 2, 2012 at 7:10
  • $\begingroup$ @Raphael: One potential reason is that--especially in multi-threaded applications it's much easier to reason about mutable structures whose identity is immutable, than those whose content and identity are both mutable. Among other things, it's very important to prevent any situation where two methods which think they are working with the same object are in fact modifying different objects, but in fact one has a reference to an object which has been superseded by the other. Never having any object get superseded avoids that problem. $\endgroup$
    – supercat
    Jan 30, 2014 at 17:55
  • $\begingroup$ If hypothetically some system always used the same g(x) secondary hash function independent of the value of h(x), then that system would have the problems you describe. That may be why, when people actually do implement dynamic perfect hashing, they use a different secondary hash function for every one of the secondary hash tables. When the hash functions are chosen and re-chosen appropriately, they don't need any methodology to chain the contents. $\endgroup$
    – David Cary
    Aug 13, 2016 at 3:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.