我被要求远离HashMap或任何类型的Hashing。
问题就是这样 -
假设您有最多20位小数的PRODUCT ID以及产品说明。如果不使用地图或任何类型的散列函数,那么存储/检索这些产品ID及其描述的最佳/最有效方法是什么?
为什么在这种情况下使用Maps不是一个好主意?
您将解决方案出售给亚马逊会有什么变化?
答案 0 :(得分:11)
插入/删除/查找操作交错时,可以使用映射。 O(log n)中的每个操作都摊销。
在您的例子中,您只进行搜索操作。您可能会认为任何数据库更新(插入/删除产品)都不会发生这么多时间。因此,面试官可能希望您获得最佳的查找操作数据结构。
在这种情况下,我只能看到其他答案中已提出的一些内容:
使用trie,如果产品ID不共享公共前缀,则很有可能仅查看前缀的第一个字符(或仅显示第一个字符)来查找产品描述。例如,让我们使用125个产品获取该产品ID列表:
假设您正在寻找在您的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尽可能地散乱,那么它将是最好的。