给出一个列表,说“ x”的长度为n,那么以下算法的时间复杂度是多少?
def foo(x):
n = len(x)
if n <= 1:
return 17
return foo(x[:n//2]) + foo(x[n//2:])
答案是:
O(n log n)
但是我不知道为什么吗? 我很难弄清楚我们使用递归的最后一行,我知道每次它将列表的长度减少一半,所以它的O(log n),但是它在每次迭代中都添加了另一个递归,它也是O(log n) ),因此我虽然使用了O(log n log n),但不幸的是不是。
答案 0 :(得分:2)
您可以正确地确定是 O(登录n),但是您无法确定它是什么。 它是达到基本情况所需的步骤数。由于每次将列表切成两半,因此每次调用foo
时,您使用的列表的大小是您刚拥有的列表的一半。因此,需要O(log n)步才能达到基本情况。
下一个问题是:每个步骤要完成多少工作?第一步,将列表分成两半,这需要n
个内存副本。在第二步中,将大小为n/2
的两个列表分成两半。完成的工作量保持不变!从一个步骤到下一个步骤,您要削减的每个列表的大小为一半(由于调用了foo(n//2)
),但是您必须这样做的列表数量要加倍(因为您递归调用foo
两次)。因此,对于每一步,您总是在做O(n)工作。
O(log n)个步骤*每个步骤中O(n)的工作量=总共O(n log n)个。
答案 1 :(得分:0)
这类似于合并排序。在这里,您花费 O(n)时间来切片数组 as seen here,然后对列表的两半进行操作。合并排序的时间复杂度为O(n log(n))。
如果您想派生合并排序,可以看看this
答案 2 :(得分:0)
此算法为列表O(n)
中的每个元素返回一次,然后还实现了对分搜索O(log n)
,因此,它是O(n log n)
但是我不知道为什么吗?我很难弄清楚我们使用递归的最后一行,我知道每次它将列表的长度减少一半,所以它的O(log n),但是它在每次迭代中都添加了另一个递归,它也是O(log n) ),因此我虽然使用了O(log n log n),但不幸的是不是。
O(log n) + O(log n)
= O(log n)
为此添加一两个打印语句将有很大帮助:
def foo(x):
n = len(x)
print(x)
if n <= 1:
print("return")
return 17
return foo(x[:n//2]) + foo(x[n//2:])
>>> foo([1,2,3,4,5,6,7,8])
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4]
[1, 2]
[1]
Return
[2]
Return
[3, 4]
[3]
Return
[4]
Return
[5, 6, 7, 8]
[5, 6]
[5]
Return
[6]
Return
[7, 8]
[7]
Return
[8]
Return
很显然,这会为列表中的每个元素返回一次,这使其至少为O(n)
。此外,要以两分搜索类型拆分列表,需要O(log n)
答案 3 :(得分:0)
def foo(x):
n = len(x) # outer
if n <= 1: # inner
return 17 # inner
return foo(x[:n//2]) + foo(x[n//2:]) #outer
我们可以将功能分为2部分。第一个外部部分可以用“ n = len(x)”和“ return foo(x [:n // 2])+ foo(x [n // 2:])”定义,其中“ x”是递归分为2个。因此,外部函数为log n。在第二部分中,内部部分由“ if n <= 1:\ return 17”组成,其中n用“ n <= 1”搜索。因此内部函数只是n。结果,内部x外部给出了“ n.log n”。