输入:正整数列表,其中一个条目恰好出现一次,所有其他条目恰好出现两次(例如[1,3,2,5,3,4,1,2,4])
输出:唯一条目(上例中为5)
以下算法应该是O(m)时间和O(1)空间,其中m是列表的大小。
def get_unique(intlist):
unique_val = 0
for int in intlist:
unique_val ^= int
return unique_val
我的分析:给定一个长度为m的列表,输入列表中将有(m + 1)/ 2个唯一正整数,因此列表中可能的最小整数将是(m + 1)/ 2。如果我们假设这个最好的情况,那么当采用XOR和时,变量unique_val将需要内存中的ceiling(log((m + 1)/ 2))位,所以我认为空间复杂度应至少为O(log(m) ))。
答案 0 :(得分:3)
你的分析肯定是一个正确的答案,特别是在像Python这样优雅地处理任意大数字的语言中。
在考虑空间和时间复杂性时,要清楚自己要测量的内容非常重要。一个合理的假设可能是整数的大小是常数(例如,您使用的是64位整数)。在这种情况下,空间复杂度肯定是O(1),但时间复杂度仍然是O(m)。
现在,您还可以争辩说使用固定大小的整数意味着您在m
的大小上有一个常量上限,因此时间复杂度也可能是O(1)。但是在大多数情况下,你需要分析这种算法的运行时间,你可能对长度为10和长度为10亿的列表之间的差异非常感兴趣。
我认为在分析空间和时间复杂性时澄清并陈述您的假设非常重要。在这种情况下,我假设我们有一个固定大小的整数,值m
远小于最大整数值。在这种情况下,O(1)空间和O(m)时间可能是最好的答案。
编辑(基于其他答案中的讨论)
由于所有m
都是下限没有列表中的最大值,因此您实际上无法提供最坏情况的空间估计。即列表中的数字可以任意大。要对此算法的空间复杂性有任何合理的答案,您需要对输入值的最大大小做出一些假设。
答案 1 :(得分:2)
(空间/时间)复杂度分析通常应用于更高级别的算法。虽然您可以下载到特定的语言实现级别,但它可能在所有情况下都没有用。
您的分析既正确又可能错误。它适用于当前的cpython实现,其中整数没有最大值。如果所有整数都相对较小并且适合特定于实现的小数字的情况,那就没关系。
但它不一定对python的所有其他实现都有效。例如,您可以使用优化实现来确定intlist
未再次使用,而不是使用unique_val
,它会重用已使用列表元素的空间。 (基本上将此函数转换为空间优化的reduce调用)
然后,我们甚至可以用分配的整数来讨论GC'd语言中的空间复杂性吗?您对复杂性的分析是错误的,因为a ^= b
将为大值b
分配新内存,其大小取决于系统,体系结构,python版本和运气。
然而,您的原始问题是“为什么以下算法是O(1)空间?”。如果您查看算法本身并假设您有一些任意的最大整数限制,或者您的语言可以代表有限空间中的任何数字,那么答案是肯定的。具有这些条件的算法本身使用恒定空间。
答案 2 :(得分:1)
算法的复杂性始终取决于您使用的机器模型(=平台)。例如。我们常说加倍和除以IEEE浮点数是运行时复杂度O(1) - 情况并非总是如此(例如在没有FPU的8086处理器上)。
对于上述算法,只要您的输入列表没有元素>,空间复杂度O(1)就会成立。 2147483647(= sys.maxint)。通常,python将整数存储为带符号的32位值。对于这些数据类型,您的处理器已经在硬件中实现了所有相关操作,并且通常只需要恒定数量的时钟周期(在大多数情况下只有一个)来执行它们(=运行时复杂度O(1))并且只有一个常量占用内存地址(仅一个)来存储结果(=空间复杂度O(1))。
但是,如果您的输入超过2147483647,则python通常使用软件实现的数据类型来存储这些大整数。对它们的操作不再在O(1)中,并且它们需要的不仅仅是常数O(1)空间。