我在最近的一次采访中被问到这个问题

时间:2009-10-11 15:17:18

标签: algorithm data-structures

我被要求远离HashMap或任何类型的Hashing。

问题就是这样 -

假设您有最多20位小数的PRODUCT ID以及产品说明。如果不使用地图或任何类型的散列函数,那么存储/检索这些产品ID及其描述的最佳/最有效方法是什么?

为什么在这种情况下使用Maps不是一个好主意?

您将解决方案出售给亚马逊会有什么变化?

14 个答案:

答案 0 :(得分:11)

插入/删除/查找操作交错时,可以使用映射。 O(log n)中的每个操作都摊销

在您的例子中,您只进行搜索操作。您可能会认为任何数据库更新(插入/删除产品)都不会发生这么多时间。因此,面试官可能希望您获得最佳的查找操作数据结构。

在这种情况下,我只能看到其他答案中已提出的一些内容:

  • 排序数组(进行二分查找)
  • Hasmap
  • trie

使用trie,如果产品ID不共享公共前缀,则很有可能仅查看前缀的第一个字符(或仅显示第一个字符)来查找产品描述。例如,让我们使用125个产品获取该产品ID列表:

  • “1”
  • “2”
  • “3”
    ...
  • “123”
  • “124”
  • “1234567”

假设您正在寻找在您的trie中标题为“1234567”的产品ID,只查看第一个字母:“1”然后“2”然后“3”然后“4”将导致良好的产品描述。因为没有其他可能性,所以无需阅读剩余的产品ID。 将产品ID长度视为n,您的查找将在O(n)中。但正如上面解释的那样,可以更快地检索产品描述。由于产品ID的大小有限(20个字符),因此特里高度将限制在20个等级。这实际上意味着你可以认为查找操作永远不会超过一个恒定的时间,因为你的搜索永远不会超过trie height => O(1)。虽然任何BST查找最多摊销O(日志N),N是树中的项目数

虽然散列图可能会导致查找速度变慢,因为您需要使用散列函数计算索引,该散列函数可能是在读取整个产品ID长度时实现的。加上在与其他产品ID发生碰撞时浏览列表。

对已排序的数组执行二进制搜索,查找操作中的性能取决于数据库中的项目数。

答案 1 :(得分:6)

我认为是B-Tree。这仍然算作地图吗?

主要是因为您可以在内存中同时加载许多项目。在内存中搜索这些项目非常快。

答案 2 :(得分:4)

连续的整数给出了哈希映射的完美选择,但它只有一个问题,因为它默认没有多线程访问。此外,由于在您的问题中提及亚马逊,我可能认为您需要考虑到可靠性和RAM限制问题。

你在回答这个问题时可能会做的是解释一下 你不能使用任何内置的数据存储方案,你所能做的只是“模仿”一个。

所以,假设您有M = 10 ^ 20个产品及其编号和描述。 您可以将此集分区为N个子集的组。 然后你可以组织M / N容器,这些容器的元素数量会大大减少。递归地使用这个想法将为您提供一种方法,将整个集合存储在具有此类属性的容器中,以便访问它们可以接受性能。

为了说明这个想法,考虑一个只有20个元素的小例子。 我希望你能想象一下目录为“1”,“2”,“3”,“4”的文件系统。 在每个目录中,您可以通过以下方式将产品描述存储为文件:

folder 1: files 1 to 5
folder 2: files 6 to 10
...
folder 4: files 16 to 20

然后您的搜索只需要两个步骤即可找到该文件。 首先,通过除以20/5(您的M / N)来搜索正确的文件夹。 然后,使用给定的ID读取存储在文件中的产品描述。

这只是一个非常粗略的描述,但是,这个想法非常直观。 所以,也许这就是你的面试官想要听到的。

就我自己而言,当我在面试中遇到这样的问题时,即使我没有正确地得到问题(这是最糟糕的情况:) :)我总是试图从面试官那里得到正确答案。

答案 3 :(得分:2)

最好/最有效率的是什么?本来是我的回答。

E.g。为了存储它们,可能快速做的是两个数组,每个数组有20个元素。一个用于ID,用于描述。对这些进行迭代非常快。这是有效的记忆。

当然,对于任何实际应用来说,解决方案都是无用的,但问题也是如此。

答案 4 :(得分:1)

B-Tree有一个有趣的替代方案:Radix Tree

答案 5 :(得分:1)

我认为他想要你做什么,而且我说这是一个好主意,就是使用计算机内存空间。

如果您使用64位(虚拟)内存地址,并假设您拥有数据的所有地址空间(从不情况),则可以存储一个字节的值。

您可以将ProductID用作地址,将其转换为指针,然后获取该字节,这可能是另一个内存中实际数据的偏移量。

不会这样做,但也许这就是他们正在寻找的答案。

阿萨夫

答案 6 :(得分:1)

我想知道他们是否希望您注意到在电子商务应用程序(例如亚马逊)中,常见的用例是“反向查找”:使用描述检索产品ID。为此,使用反向索引,其中描述中的每个关键字是索引关键字,其与相关产品标识符的列表相关联。二叉树或跳过列表是索引这些关键词的好方法。

关于产品标识符索引:实际上,B-Trees(二进制搜索树)将用于基于磁盘的大型20位标识符索引。但是,他们可能一直在寻找可以在RAM中实现的玩具解决方案。由于十进制数字的“字母表”非常小,所以它非常适合于特里。

答案 7 :(得分:0)

如果散列函数为您提供现有键的散列值的非常均匀的分布,则散列图的效果非常好。使用非常糟糕的哈希函数,可能会发生这样的情况,即20个值的哈希值将相同,这会将检索时间推迟到O(n)。另一方面,二进制搜索保证你O(log n),但插入数据更加昂贵。

所有这些都是非常增量的,您的数据集越大,密钥分发错误的可能性就越小(如果您使用的是经过验证的优秀哈希算法),并且在较小的数据集上,O(n)之间的差异和O(log n)不用担心。

答案 8 :(得分:0)

  

20个十进制PRODUCT ID以及产品说明

简单的线性搜索会非常好......

我会创建一个带有id的简单数组。和其他带数据的数组。

线性搜索少量密钥(20!)比任何二叉树或散列更有效。

答案 9 :(得分:0)

如果大小有限,有时使用排序列表会更快。

当您使用Hash-anything时,首先必须计算哈希值,然后找到哈希桶,然后对桶中的所有元素使用equals。所以这一切都加起来。

另一方面,您可以只使用一个简单的ArrayList(或适用于该应用程序的任何其他List flavor),使用java.util.Collections.sort对其进行排序,并使用java.util.Collections.binarySearch来查找元素。

但正如Artyom指出的那样,在这种情况下,简单的线性搜索可能要快得多。

另一方面,从可维护性的角度来看,我通常会在这里使用HashMap(或LinkedHashMap),并且只有在探查器告诉我这样做时才会执行特殊操作。此外,20个集合随着时间的推移趋向于成为20000的集合,所有这些优化都将被浪费。

答案 10 :(得分:0)

对于这种情况,散列或B树没有任何问题 - 你的面试官可能只是想让你思考一下,而不是出现预期的答案。当采访者希望候选人思考时,这是一个好兆头。它表明组织重视思想,而不仅仅是从CS0210的讲义中剔除一些东西。

顺便说一句,我假设“20十进制产品ID”表示“产品ID的大集合,其格式为20个十进制字符”....因为如果只有20个十进制字符,那么考虑到算法。如果你不能使用散列或Btrees代码进行线性搜索并继续前进。如果您愿意,可以对数组进行排序,并使用二进制搜索。

但如果我的假设是正确的,那么采访者所要求的似乎就是围绕哈希图的时间/空间权衡。可以改进哈希映射的时间/空间曲线 - 哈希映射确实存在冲突。因此,您可以通过将20个十进制数字转换为数字来获得一些改进,并将其用作稀疏填充数组的索引......这是一个非常大的数组。 :)

将它卖给亚马逊?祝你好运。无论你提出什么,都必须具有可专利性,而且这个讨论中的任何内容似乎都没有达到那个水平。

答案 11 :(得分:0)

我根据他们对产品ID和两位数的回答感觉他们正在寻找的答案是将数字产品ID转换为不同的基本系统或打包形式。

他们指出产品描述与产品ID有关,告诉您可以在当前字段数据类型中使用更高的基本系统。

答案 12 :(得分:0)

你的面试官可能正在寻找一个特里。如果你的密钥有一个[小]常数上限,那么你有O(1)插入和查找。

答案 13 :(得分:0)

  

我想他想要你做什么,而且   我不是说这是一个好主意,是的   使用计算机内存空间。

     

如果使用64位(虚拟)内存   地址,并假设你拥有所有   地址空间为您的数据(即   从来没有这种情况)你可以存储一个   一个字节的值。

不幸的是,2 ^ 64 =约= 1.8 * 10 ^ 19。略低于10 ^ 20。巧合?

log2(10 ^ 20)= 66.43。

这是一个有点邪恶的提议。

好的,2 ^ 64 可以放在内存空间内。

假设描述的N个字节的边界,比如说N = 200。 (谁想在他们寻找烤面包机时下载Anna Karenina?) Commandeer 8 * N 64位机器,内存繁重。亚马逊可以解决这个问题。

每台机器在其(非常稀疏的)位图中加载所有描述的描述文本的一位。让MMU /虚拟内存处理稀疏性。

将产品标签广播为59位数字,并将位掩码广播为一个字节。 (59 = ceil(log2(10 ^ 20)) - 8)

每台机器从产品说明中返回一位。查找是虚拟内存解除引用。你甚至可以插入和删除。

当然,在某些时候,分页将开始成为一个婊子!

奇怪的是,如果产品ID尽可能地散乱,那么它将是最好的。