在此在线教科书https://runestone.academy/runestone/static/pythonds/SortSearch/TheMergeSort.html中,他们为mergesort提供了以下代码:
def mergeSort(alist):
if len(alist)>1:
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
mergeSort(lefthalf)
mergeSort(righthalf)
i=0
j=0
k=0
while i < len(lefthalf) and j < len(righthalf):
if lefthalf[i] <= righthalf[j]:
alist[k]=lefthalf[i]
i=i+1
else:
alist[k]=righthalf[j]
j=j+1
k=k+1
while i < len(lefthalf):
alist[k]=lefthalf[i]
i=i+1
k=k+1
while j < len(righthalf):
alist[k]=righthalf[j]
j=j+1
k=k+1
在分析中,在线图书让他们写:
回想一下,切片运算符为O(k),其中k是切片的大小。为了保证mergeSort将是O(nlogn),我们需要删除slice运算符。同样,如果我们在进行递归调用时简单地将起始索引和结束索引与列表一起传递,就有可能做到这一点。
所以我的第一个问题是:
1-你们能告诉我切片运算符破坏算法时间复杂性的情况吗?
我编写的代码无需下面的切片操作即可完成
def mergeSort2(alist, l, r):
if r - l >= 1:
mid = l + (r - l)//2
mergeSort2(alist, l, mid)
mergeSort2(alist, mid+1, r)
i = l
j = mid+1
k = 0
temp_list = [None]*(r-l+1)
while i < mid+1 and j < r+1:
if alist[i] <= alist[j]:
temp_list[k] = alist[i]
i=i+1
else:
temp_list[k] = alist[j]
j=j+1
k=k+1
while i < mid+1:
temp_list[k] = alist[i]
i=i+1
k=k+1
while j < r+1:
temp_list[k] = alist[j]
j=j+1
k=k+1
n = 0
for index in range(l,r+1):
alist[index] = temp_list[n]
n += 1
如您所见,代码底部的循环可以被切片代替。
问题2:
2-我看到它的方式,而不是在递归调用之前做两个需要k
时间的切片,现在我们在temp_list
时间内初始化k
,然后复制排序后的{{ 1}}放入temp_list
时间的结果中。那么算法的时间复杂度没有改变吗?
答案 0 :(得分:1)
切片在数量级上根本不影响时间复杂度。不变因素是另一个讨论。
了解时间复杂度如何不变的关键部分在这里:
def mergeSort(alist):
if len(alist)>1:
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
mergeSort(lefthalf)
mergeSort(righthalf)
所以让我们一步一步地将其分解:
在这里,我们复制整个列表。这是O(N)
lefthalf = alist[:mid]
righthalf = alist[mid:]
此部分在左侧和右侧调用mergeSort
,从而导致递归切片。
mergeSort(lefthalf)
mergeSort(righthalf)
mergeSort(lefthalf)
和mergeSort(righthalf)
一起将对整个数组进行切片,并且它们的递归子级的总和也将如此。关键是要对数组进行整体切片的次数是log N
,因为您不断将数组除以2,因此只能进行log N
次。这使您有O(N)
的切片时间O(log N)
的切片时间,从而得到O(NlogN)
总结:
否,如果正确实现了切片。显然,您可以添加随机片并弄乱时间复杂度。
切片不会改变您的时间复杂程度-仍然O(NlogN)