我在Python / C ++中实现了一个mergesort(来自interactivepython)。代码完全有效,但我的问题是,我似乎无法弄清楚为什么代码的特定部分确实有效。
代码是:
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
plist = [54,26,93,17]
mergeSort(plist)
print(plist)
在lefthalf = [54 26]之后,进一步的子程序分裂为lefthalf = [54]和righthalf = [26],合并代码用于提供alist = [26,54](这是排序的左半部分)。
现在我在调试窗口中执行下一步,我得到一个lefthalf = [26,54]。这怎么可能,因为第一个调试显示它之前定义为[54,26]。 [26,54]的更新发生在哪里?
任何帮助都会受到超级赞赏!
答案 0 :(得分:0)
python中的列表在函数内部是可变的(即,它们的内容可以改变)。当在函数内部更改列表时,更改发生在您调用函数的列表中,因为它是相同的列表。
因此,每次在第一次调用或任何递归调用中操作alist
或alist
切片时,都会对列表进行更新。
编辑:删除误导位
答案 1 :(得分:0)
您的mergesort
函数会修改您传递给它的列表。也就是说,它会更改内容以使项目按顺序排列,而不是返回新列表。
这是您在递归调用期间在调试器中看到的内容。在递归的第一级中,通过从原始列表复制一些值(使用切片语法)来创建lefthalf
。它开始包含[54, 26]
。然后将此列表传递给mergesort
的另一个调用。请注意,命名可能会令人困惑,因为在内部调用中它将列表称为alist
(并且它有自己独立的lefthalf
列表)。当内部调用返回时,外部调用lefthalf
的内容结果被修改为[26, 54]
(它们按顺序排列,这就是我们想要的!)。
可能是您的调试器在返回发生时没有明确说明。因为它的所有功能都相同(由于递归),所以当内部调用结束并且外部调用的控制流程恢复时,它可能并不明显。
这是您的代码的演练,其中我在您对示例列表进行排序时显示递归的各个级别中的不同变量的值。请注意,这不是可运行的Python代码,我缩进指示递归级别,而不是控制流。为了使示例相对较短,我还省略了一些步骤,例如比较两个子列表中的值以及在合并过程中更新i
j
和k
索引:
plist = [54,26,93,17]
mergesort(plist)
# alist is a referece to plist which contains [54,26,93,17]
lefthalf = alist[:mid] # new list which initially contains [54,26]
righthalf = alist[mid:] # new list which initially contains [93,17]
mergesort(lefthalf)
# alist is a reference to the outer lefthalf list, which contains [54,26]
lefthalf = alist[:mid] # new list, initially contains [54]
righthalf = alist[mid:] # new list, initially contains [26]
mergesort(lefthalf)
# alist is a reference to the previous level's lefthalf, [54]
# the if statement doesn't pass its test, so we do nothing here (base case)
# lefthalf was not changed by the recursive call
mergesort(righthalf)
# alist is a reference to the previous level's righthalf, [26]
# base case again
# righthalf was not changed
alist[k]=righthalf[j] # now we merge lefthalf and righthalf back into alist
alist[k]=lefthalf[i] # these statements change the contents of alist
# lefthalf's contents changed, it is now sorted, [26,54]
mergesort(righthalf)
# alist is a reference to the outer righthalf list, which contains [93,17]
lefthalf = alist[:mid] # new list, initially contains [93]
righthalf = alist[mid:] # new list, initially contains [17]
mergesort(lefthalf) # base case, nothing happens (I'll skip the details)
mergesort(righthalf) # base case, nothing happens
alist[k]=righthalf[j] # merge lefthalf and righthalf back into alist
alist[k]=lefthalf[i] # we change the contents of alist to [17,93]
# righthalf's contents changed, it is now sorted, [17,93]
alist[k]=righthalf[j] # merge lefthalf and righthalf back into alist (more steps)
alist[k]=lefthalf[i]
alist[k]=lefthalf[i]
alist[k]=righthalf[j] # after these statements, alist is [17,26,54,93]
# plists's contents are changed so it contains [17,26,54,93]
这可能有助于您从这种复杂的递归情况中稍稍退一步,并查看一个更简单的示例,以确保您了解列表是如何变异的:
a = [1, 2] # create a list object with some initial contents
b = a # b refers to the same list object as a, nothing is copied
b[1] = 3 # this modifies the list object itself, replacing the 2 with a 3
print(a) # prints [1, 3] even through we're looking at a rather than b
def func(lst): # lst will be a new name bound to whatever is passed to the function
lst[1] = 4 # we can modify that passed-in object (assuming it's the right type)
func(a) # lst in the function will become another name for the list object
print(a) # prints [1, 4]