(Note: this response was sent before the OP removed its comment in templatetypedef: “As for integer size, we can assume that it is 32-bit unsigned.”)
Y-fast trie (see my other answer) can lead us to O(log log U + log log U) search time, which means that if your range is in billions, we look at almost 5 + 5 = 10 iterations to search.
But there is a way to achieve practical search time for 5 iterations.
Hash all combinations of the leftmost 17 bits. Direct these 131,072 keys to X-fast attempts (see My other answer) with a maximum height of 15 and a maximum space of m * 15 , where m is the number of elements in this particular bucket. Attempts will only contain the most 15 bits of each corresponding list item. Since these X-fast attempts are limited in size, the search time will be increased by 1 + log 15 = 5 . If your list does not exceed 32,768 items, the space will be almost 131,072 + n * 15 , which is slightly larger than the requested n * log n ; but since the hash height and max three are constant, the asymptotic spatial complexity is actually O(n) , and with a list of 32,768 elements or more, the complexity of the space will be practically less than n * log n .
Here is a rough sketch in the JavaScript tree of X-fast:
function pad(width, string, padding) { return (width <= string.length) ? string : pad(width, padding + string, padding); } function makeXFastTree(elems){ var xfast = {}; var height = Math.floor(Math.log2(Math.max.apply(null, elems))) + 1; function insert(x){ var y = pad(height,x.toString(2),'0'); var l = 1; var d = y.substr(-l,1);
Output:
var t = makeXFastTree([31,27,10,5,4,2,1]); console.log(JSON.stringify(t[0])); {"0":["0","1"],"1":["11011","1"],"11":["0","1"],"110":["11011","1"],"111":["11111","1"] ,"1101":["11011","11011"],"1111":["11111","11111"],"0101":["01010","01010"] ,"010":["01010","1"],"01":["0","01010"],"0010":["00100","00101"],"001":["0","00101"] ,"00":["0","1"],"0001":["00010","00010"],"000":["0","1"],"0000":["00001","00001"]} console.log(find(t[0],t[1],28)); ["11111", "1"] console.log(find(t[0],t[1],3)); ["00010", "00010"]