我最近在海湾地区(美国加利福尼亚州)与一家公司进行了面谈。其中一个问题是简单地找出一个字符串是否有重复的字符(我简化了一个冗长的问题)。
eg: input : "qwerrty" output : True
我用python编写代码。
我给出了一个解决方案,它使用一个集来跟踪迭代过程中遇到的元素。
然而,采访者希望我使用跟踪遇到的字符的数组[255]。
虽然我很乐意使用其中任何一个,但我的意见是使用一个集合只是因为我们在使用数组时浪费了255个字符空间。这是因为(我们都知道)最初我们创建一个arr [255] = 0,所有元素都为零,然后将ASCII等效索引值增加1。
另一方面,一个集合只会在访问过的元素上花费内存。
由于他(有点)争论在一组中使用数组,我很想知道他是否在技术上是正确的。在这种情况下,数组是否优先于集合/映射?如果是这样,为什么?
答案 0 :(得分:3)
关于这个问题需要注意的一点是,如果字符串中只有C个可能的不同字符,那么对于长度为C + 1或更大的任何字符串,您可以自动返回存在重复的字符看着这个字符串,因为它们有太多的字符都是独一无二的(这就是工作中的鸽子原理)。这对于思考这个特定问题的结构很重要。
接下来,请注意,您甚至不需要一堆计数器。你可以在每个角色中获得一位,因为你只需要知道在迭代数组时你是否从未见过一个字符(0)或者在(1)之前看过它。这意味着每个角色需要一位。如果您的字大小为W,这意味着您需要基于阵列的解决方案的大致C / W存储空间的总机器字。
让我们假设您在具有32位字长(W = 32)的机器上使用C = 256(例如,每个字符是一个字节的值) 。这意味着你需要8个机器字来存储位数组,这是一个可以忽略不计的存储空间,可以很容易地初始化为0.现在,考虑一下你的set实现。如果使用哈希表,将会有某种内部数组用于存储所有内容。您还需要空间来存储有关散列函数的信息,通常您可以在某处缓存集合的大小。这只是因为大小和哈希函数信息会占用三个机器字的东西,这会留下五个字的空间。如果哈希表是一般性地实现的,并且每个条目都使用一个机器字,那么如果你有一个四个或更少的哈希表,你的方法只能节省空间,这是不太可能发生的。如果你的哈希表被优化并直接存储char值,那么你最多可以存储五个单词'值得角色(20个字符)没有任何碰撞,但如果你试图保持低负荷系数,你可能会在看到10个左右的字符后重新调整表格大小。简而言之,除非你有一个非常短字符串,否则哈希表方法可能会使用 more 内存,并且散列的开销会很高。阵列方法可能更快。
另一方面,假设您在字符串中存储了任意Unicode字符。现在,C = 1,114,112(谢谢,维基百科),即使是64位字大小,你也在谈论需要一个17,408个机器字的数组来存储每个可能字符一位。这是很多的存储空间,它需要一段时间来初始化它。现在,如果你输入的字符串是"合理的"而不是病态构造的,很可能你很早就会在字符串中找到一个重复的元素(如果字符串是完全随机的,那么生日悖论你只需要√(2C)个字符在您平均获得重复之前,所以构建哈希表可能需要更少的空间。如果字符串是病态构造的,以便每个字符都是唯一的,那么计算哈希函数的常量因子开销,哈希表调整大小等可能意味着你的方法将比基于数组的方法慢,但是这是一个不寻常的用例。
总结:
如果可能的字符数很少(想想ASCII),那么基于阵列的方法可能会更快,更节省内存。
如果可能的字符数很大(想想Unicode),基于数组的方法在合理的输入上可能会变慢并且内存效率会降低,但对于病理选择的输入可能比基于散列的方法。
现在,那说,你可以说,除非代码是在一个紧密的循环中运行,否则除了"只需使用一个集合"使代码难以阅读,从而最大限度地提高整体程序效率。出于这个原因,一个合理的答案是"使用该集合,除非有理由不这样做,然后只有在数据支持的情况下才切换到基于数组的集合。"
答案 1 :(得分:2)
我认为时间复杂性与空间复杂性分析是面试官寻找的实际答案。 在空间方面,两种情况都是O(N)。 时间方面,在集合中添加字符不是O(1),但在数组中的值中加1是O(1)。 所以一般来说,使用数组会消耗相同数量的内存,但时间会少得多。