我试图从短算法中理解时间和空间复杂性之间的差异。此代码采用列表/数组并返回发生奇数次的单个数字。在Python中:
def foo(array_of_ints):
hash_table = dict()
for i in array_of_ints:
hash_table[i] = hash_table.setdefault(i, 0) + 1
for key, value in hash_table.items():
if value%2 != 0:
return key
有两个for循环遍历数组的长度。但是第一个循环只是在内存中创建一个哈希表/字典。时间复杂度O(n ^ 2)也是如此,因为我们两次遍历长度为n的数组,或者每个O(n)是时间复杂度和空间复杂度,因为第一个循环只是在内存中创建一个新的数据结构?
答案 0 :(得分:3)
时间复杂度O(n ^ 2)也是如此,因为我们两次迭代一次 长度为n的数组
您的代码的时间复杂度不是O(N²)
。
在时间复杂度上,对长度为N
的集合进行迭代被视为O(N)
。原因在于,在Big-Oh表示法中,您总是对支配函数的术语感兴趣。当你达到足够大的N
时,常数开始变得不那么有影响力了。
这不表示他们“不重要”;只有当N
倾向于无穷大时,这些常数的实际效果更类似于向海洋中添加一滴水或一桶水。这种表示法旨在让您粗略(不准确)理解算法所期望的行为-i.e.随着输入大小的增加,扩展的程度如何。
这意味着您的函数可以遍历同一个集合5次,它将是O(5N)
,仍然是O(N)
。
但是你怎么得到O(N²)
呢?您将开始将这些视为开始将循环嵌套在另一个内部。
A
- O(N)
def linear(integers):
# no nesting
for i in integers: print(i)
for i in integers: print(i+1)
B
- O(N²)
def quadratic(integers):
# notice double nesting
for i in integers:
for j in integers:
print(i+j)
C
- O(N³)
def cubed(integers):
# notice triple-nesting
for i in integers:
for j in integers:
for k in integers:
print(i+j+k)
如果你使用矩阵,你会发现O(N³)
算法的例子,至少如果你使用的是天真的实现。如果您不清楚,则称为asymptotic notation。
另请注意,Big-Oh notation表示算法运行时的上限。这意味着它可以衡量它的最差案例情景。
例如,线性搜索列表中不存在的项将使您的算法达到O(N)
的上限,因为它必须检查列表中的每个元素。
或每个时间复杂度和空间复杂度> O(n),因为第一个循环只是在内存中创建一个新的数据结构?
在这种情况下,循环所做的事情本身与测量无关。相关的是在这里占主导地位的操作,它是使循环工作的比较和增量。例如,value % 2 != 0
是一个恒定时间操作¹,或O(1)
,并且不会对代码的运行时间做出任何实质性贡献。
那么空间复杂性是什么?
所需空间还取决于输入的大小和内容。算法的最坏情况是 distinct 整数数组,这意味着每个值都是唯一的。
如果每个值都是唯一的,则每个值都将导致添加键/值对。因此,该算法需要O(N)
空间。
请注意,它可能会更少,但big-O表示法会传达上部绑定。
请记住,通常情况下,还需要在时间/空间限制之间进行权衡,其中更快的算法可能需要更多内存,而更多内存效率更高的替代方案可能需要更多时间。
¹这假设我们已将+
,-
,/
,*
,%
等算术运算定义为 basic操作,这是常见的。
答案 1 :(得分:0)
在大O表示法中,2n
和n
不被视为不同,因此算法具有O(n)
行为。要具有O(n^2)
行为,您的算法必须为数组中的每个元素遍历/构造整个数组一次。另一种说法是,你需要嵌套循环来实现O(n^2)
。
答案 2 :(得分:-1)
时间和空间复杂度都是O(n)。