Python 这两种mergesort实现的空间复杂度相同吗?
您好,我想让您告诉我这两种mergesort算法的空间复杂度是否相同 算法1:Python 这两种mergesort实现的空间复杂度相同吗?,python,algorithm,sorting,mergesort,Python,Algorithm,Sorting,Mergesort,您好,我想让您告诉我这两种mergesort算法的空间复杂度是否相同 算法1: def mergeSort(alist, l, r): if r - l >= 1: mid = l + (r - l)//2 mergeSort(alist, l, mid) mergeSort(alist, mid+1, r) i = l j = mid+1 k = 0 temp_li
def mergeSort(alist, l, r):
if r - l >= 1:
mid = l + (r - l)//2
mergeSort(alist, l, mid)
mergeSort(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
def合并排序(列表、左、右):
如果r-l>=1:
mid=l+(r-l)//2
合并排序(列表、l、mid)
合并排序(列表,中间+1,r)
i=l
j=中间+1
k=0
临时列表=[None]*(r-l+1)
当i 如果先使用lefthalf[i],那么假设我们有完美的垃圾收集,并且每个列表在停止使用后都会立即被释放
在此假设下,算法具有相同的大O空间复杂度
算法2
首先查看算法2,并考虑下面的例子:
假设您正在对长度为16的列表进行排序
[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]
计算列表的上半部分和下半部分:
[15,14,13,12,11,10,9,8] [7,6,5,4,3,2,1,0]
然后对前半部分进行排序,特别是将其划分为两个新的子列表:
[15,14,13,12] [11,10,9,8]
你也会这样做:
[15,14] [13,12]
再说一遍:
[15] [14]
只有这样,您才能开始合并列表
算法在该点分配的列表总长度是多少?
它是16+2*8+2*4+2*2+2*1
。一般来说,它是N+2N/2+2N/4+2N/8+…+2
。这是一个简单的几何级数,和大约3*N
该算法还需要调用堆栈的O(log(N))空间,但这在大O表示法中消失了:O(N)
很容易看出,这是算法在任何给定点将使用的最大值——未来将使用的已分配列表的长度(因此无法取消分配)永远不会超过3*N
算法1
再考虑一下同样的例子。我们将对下面的列表进行排序
[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]
想象一下,我们已经对其上半部分和下半部分进行了排序:
[8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7]
现在,我们需要分配一个长度为N的临时列表来执行合并。
所以在那一刻,我们积极地使用了两个长度为N的列表,这给了我们2*N=O(N)
同样,很容易看出,我们永远不会使用更多的内存:对列表的一半进行排序的任务自然不会比对列表本身进行排序花费更多
结论
两种算法都使用O(N)内存。它们使用O(log(N))作为调用堆栈,但与O(N)相比,这是一个较小的开销
另外,我们知道释放未使用的对象验证了我们最初关于垃圾收集的假设。首先,让我们假设我们有完美的垃圾收集,并且每个列表在停止使用后立即被释放
在此假设下,算法具有相同的大O空间复杂度
算法2
首先查看算法2,并考虑下面的例子:
假设您正在对长度为16的列表进行排序
[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]
计算列表的上半部分和下半部分:
[15,14,13,12,11,10,9,8] [7,6,5,4,3,2,1,0]
然后对前半部分进行排序,特别是将其划分为两个新的子列表:
[15,14,13,12] [11,10,9,8]
你也会这样做:
[15,14] [13,12]
再说一遍:
[15] [14]
只有这样,您才能开始合并列表
算法在该点分配的列表总长度是多少?
它是16+2*8+2*4+2*2+2*1
。一般来说,它是N+2N/2+2N/4+2N/8+…+2
。这是一个简单的几何级数,和大约3*N
该算法还需要调用堆栈的O(log(N))空间,但这在大O表示法中消失了:O(N)
很容易看出,这是算法在任何给定点将使用的最大值——未来将使用的已分配列表的长度(因此无法取消分配)永远不会超过3*N
算法1
再考虑一下同样的例子。我们将对下面的列表进行排序
[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]
想象一下,我们已经对其上半部分和下半部分进行了排序:
[8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7]
现在,我们需要分配一个长度为N的临时列表来执行合并。
所以在那一刻,我们积极地使用了两个长度为N的列表,这给了我们2*N=O(N)
同样,很容易看出,我们永远不会使用更多的内存:对列表的一半进行排序的任务自然不会比对列表本身进行排序花费更多
结论
两种算法都使用O(N)内存。它们使用O(log(N))作为调用堆栈,但与O(N)相比,这是一个较小的开销
另外,我们知道取消分配未使用的对象验证了我们最初关于垃圾收集的假设。感谢您富有洞察力的回答。所以基本上,它们在O()表示法方面具有相同的空间复杂度,但algo1实际上在常数因子方面更好。递归树越往下,这个因素就越重要,因为当我们再往下树时,algo1只会有一个额外的大小列表n/2^k
,其中k
是深度,而algo2在同一点上会有一些更接近3n
。因此,为了结束讨论,我们可以说algo1在空间复杂度方面更好,但不是更好吗?是的,算法1在大O符号中隐藏了更好的常数(2 vs 3)。这是否重要取决于应用程序。如果要排序的数据非常大,那么常量开始起作用。但是,同样,如果数据真的很大,您可能应该寻找一种“就地”排序算法。感谢您有洞察力的回复。所以基本上,它们在O()表示法方面具有相同的空间复杂度,但algo1实际上在常数因子方面更好。递归树越往下,这个因素就越重要,因为当我们在树algo1上越往下的时候