My question is, how do they work and how do you implement it? What is
the difference between stl map, hash tables, and lookup tables.
What you're looking for is an efficient mechanism by which you can look up the value that corresponds to a given key.
Your current mechanism (a long list of if/else-if commands) is rather inefficient, in that if you have N possible values to choose from, you will (on average) have to compare your candidate key against (N/2) other keys before you find the one that matches and you can stop looking. (This is known as O(N) complexity)
So what are the other choices?
The simplest one is literally just an array of values, e.g.
const char* const myLookupTable[1000] = {
"zero",
"one",
"two",
[...]
"nine hundred and ninety-nine"
};
... with a lookup table like that, you take a key (which in this case is a number between 0 and 999, inclusive), and look up the corresponding value with a single array-lookup:
const char* val = myLookupTable[myKeyIndex];
That's super-efficient (O(1) complexity -- it always finishes in constant time, regardless of how big the array is!), but it only works in cases where your keys are unsigned integers in a continuous (and relatively small) range of values. For example, if your keys were strings, this approach wouldn't apply.
For more flexibility, the next option would be STL's std::map
. std::map
gives you fast key->value lookups from any key-type to any value-type. Internally it is implemented as a tree: each key-value pair is inserted into the tree in such a way that the tree remains sorted with the smallest keys at the left of the tree and the largest keys at the right. Because of that, looking up a key (and its associated value) in a std::map
is just a matter of starting at the tree's root node and comparing the key at that node to the key you are looking up: is it less than your key? Then move to the right-hand child. Or it greater than your key? Then move to the left-hand child. Repeat that until you get to the bottom of the tree, at which point you'll either find the key-value pair you were looking for or you'll find that it's not present. This is an algorithm of O(log(N)) complexity, because for a tree with N values in it, it takes log(N) comparisons for the lookup to complete. O(log(N)) is considered pretty good efficiency.
The final data structure you mentioned is a hash table (as seen in std::unordered_map
). A hash table does things a bit differently -- internally it is an array, but in order to avoid the limitations of the lookup-table approach, it also comes with an algorithm for figuring out where in its array a given key/value pair is to be stored. It does this by calculating a hash code for the key-object you pass in -- and then using that code to compute an offset into the array (e.g. int array_offset = hash_code % array_size
) and looking at that slot in the array to see if the requested key-value pair is there. If it is, then it's done (O(1) performance again!); or if the slot is empty, then it knows that your key isn't in the table and can return failure immediately (O(1) again). If the slot is occupied by some other key/value pair, then the hashtable will need to fall back to another algorithm to sort out the hash collision
; different hash tables handle that different ways but it's generally still fairly efficient.