我目前正在bit trie中存储大量无符号32位整数(有效地形成一个二进制树,其中包含32位值中每个位的节点。)这对于快速查找非常有效精确值。
我现在希望能够搜索可能在或不在trie中的键,并找到小于或等于搜索键的第一个键的值。 这有效可行吗?或者我应该使用不同的数据结构吗?
我正在使用trie,因为它的速度和缓存位置,理想情况下也不想牺牲。
例如,假设trie添加了两个键:
0x00AABBCC
0x00AABB00
我现在正在搜索不存在的密钥0x00AABB11
。我想在树中找到第一个键,其值为< =搜索键,在这种情况下,它将是0x00AABB00
的节点。
虽然我已经考虑过一种可能的算法,但我正在寻找具体的信息,如果它有效可能和/或是否有已知算法 ,这无疑会比我自己更好。
答案 0 :(得分:1)
我们可以将trie视为二叉搜索树。实际上,它是一个二叉搜索树。以32位trie为例,假设左子为0,右子为1.对于根,左子树为小于0x80000000的数字,右子树为不小于0x80000000的数字,依此类推等等。因此,您可以使用类似的方法来查找二进制搜索树中不大于搜索键的最大项。只是不要担心回溯,它不会回溯过多,也不会改变搜索的复杂性。 如果匹配在trie中匹配失败,只需回溯以找到失败节点的最近祖先的最右边的子节点。
答案 1 :(得分:1)
如果数据是静态的 - 你没有添加或删除项目 - 那么我会好好看看使用带二进制搜索的简单数组。你牺牲了缓存局部性,但这可能不是灾难性的。我不认为缓存本地化本身就是一个目的,而是一种使数据结构更快的方法。
通过在数组中创建平衡二叉树,可以获得更好的缓存局部性。位置0是根节点,位置1是左节点,位置2是右节点等。它与您用于二进制堆的结构相同。如果你愿意为每个节点分配另外4个字节,你可以把它变成一个左线程的二叉树,这样如果你搜索X并最终得到下一个更大的值,那么左边的线程会给你下一个更小的值。但总的来说,在一般情况下,我没有看到它的性能优于普通阵列。
很大程度上取决于您的数据稀疏程度以及范围。如果您正在查看0到40亿范围内的几千个可能值,那么二进制搜索看起来非常有吸引力。如果你正在讨论5亿个不同的值,那么我会考虑分配一个位数组(500兆字节)并使用线性后向扫描进行直接查找。这会给你非常好的缓存局部性。
答案 2 :(得分:1)
在找到项目的最佳情况下,有点trie走32个节点。
像std::map
或java.util.TreeMap这样的红黑树中的一百万个条目在每个查询中只需要log2(1,000,000)或大约20个节点,最坏的情况。并且你并不总是需要走到树的底部,使得平均情况具有吸引力。
当回溯找到<=
时,差异更加明显。
您拥有的条目越少,红黑树的情况就越好
至少,我会将任何解决方案与红黑树进行比较。