尽管有条件,但合并排序继续调用自身

时间:2016-01-02 09:20:30

标签: python recursion mergesort

我有一个非常奇怪的问题,教我自己写一些。 我已经用Python进行了这种合并排序。

我想知道你是否可以帮助我理解为什么这会继续自我调用,尽管有条件要关闭这个功能。

print arrayToSort 
#[[67], [26], [75], [80], [54], [93], [97], [96], [77], [91]]

def merge_sort(array):
    newTotalArray = []
    for x in range(0, len(array)/2):
        newSubArray = []
        lowerX = x*2
        upperX = x*2+1
        while len(array[lowerX]) > 0 or len(array[upperX]) > 0:
            if len(array[lowerX]) > 0 and len(array[upperX]) > 0:
                if array[lowerX][0] <= array[upperX][0]:
                    newSubArray.append(array[lowerX][0])
                    del array[lowerX][0]
                else:
                    newSubArray.append(array[upperX][0])
                    del array[upperX][0]
            elif len(array[lowerX]) > 0:
                newSubArray.append(array[lowerX][0])
                del array[lowerX][0]
            else:
                newSubArray.append(array[upperX][0])
                del array[upperX][0]
        newTotalArray.append(newSubArray)
    if len(array) % 2 != 0:
        newTotalArray.append(array[len(array)-1])
    print 'still going'
    print newTotalArray
    if len(newTotalArray) > 1:
        merge_sort(newTotalArray)
    print 'finished'
    print newTotalArray

merge_sort(arrayToSort)

我希望这个函数在len(newTotalArray)== 1时停止调用自己。

但是,对于这段代码,我得到了

[[67], [26], [75], [80], [54], [93], [97], [96], [77], [91]]
still going
[[26, 67], [75, 80], [54, 93], [96, 97], [77, 91]]
still going
[[26, 67, 75, 80], [54, 93, 96, 97], [77, 91]]
still going
[[26, 54, 67, 75, 80, 93, 96, 97], [77, 91]]
still going
[[26, 54, 67, 75, 77, 80, 91, 93, 96, 97]]
finished
[[26, 54, 67, 75, 77, 80, 91, 93, 96, 97]]
finished
[[], []]
finished
[[], [], []]
finished
[[], [], [], [], []]

我需要将它放在模块中并返回结果,但如果我这样做,则结果为[[], [], [], [], []]

你能帮我理解为什么会这样吗?

2 个答案:

答案 0 :(得分:1)

多次错误地达到您的“已完成”语句。一个简单的解决方法是在递归函数到达该行之前将控制权退出。在此if块中插入return关键字: -

if len(newTotalArray) > 1:
    merge_sort(newTotalArray)
    return

顺便说一下,像array[len(array)-1]之类的代码冒犯了我。请改用此表格: -

if len(array) % 2 != 0:
    newTotalArray.append(array[-1])

最后一个想法。应该避免递归,因为在运行时使用的内存是昂贵的。使用del array[0]关键字从数组的开头删除元素也比删除最后一个元素的效率低。这是Python中一个更简单的合并排序实现,它避免了递归,也避免了总是删除临时列表中的第一个元素: -

def merge_sort(array):
    print 'Original: ', array
    array = map(lambda x: [x], array)
    print 'Nested: ', array
    while len(array) > 1:
        for i in xrange(len(array)-2,-1,-2):
            newArray = []
            arrayA, indexA = array[i], 0
            arrayB, indexB = array[i+1], 0
            while indexA < len(arrayA) and indexB < len(arrayB):
                if arrayA[indexA] < arrayB[indexB]:
                    newArray.append(arrayA[indexA])
                    indexA += 1
                else:
                    newArray.append(arrayB[indexB])
                    indexB += 1
            if indexA < len(arrayA):
                newArray += arrayA[indexA:]
            if indexB < len(arrayB):
                newArray += arrayB[indexB:]
            array[i] = newArray
            del array[i+1]
        print 'Step: ', array
    return array[0]

arrayToSort = [93, 64, 16, 28, 65, 80, 42, 96, 8, 44, 1]
print merge_sort( arrayToSort )
#Original:  [93, 64, 16, 28, 65, 80, 42, 96, 8, 44, 1]
#Nested:  [[93], [64], [16], [28], [65], [80], [42], [96], [8], [44], [1]]
#Step:  [[93], [16, 64], [28, 65], [42, 80], [8, 96], [1, 44]]
#Step:  [[16, 64, 93], [28, 42, 65, 80], [1, 8, 44, 96]]
#Step:  [[16, 64, 93], [1, 8, 28, 42, 44, 65, 80, 96]]
#Step:  [[1, 8, 16, 28, 42, 44, 64, 65, 80, 93, 96]]

#[1, 8, 16, 28, 42, 44, 64, 65, 80, 93, 96]

答案 1 :(得分:1)

问题是python修改了列表而不是列表的副本(这是通过引用传递而不是传递值)。因此,每次删除条目时,都会将其从原始列表中删除,从而将其删除。它还没有打电话,只是完成了之前的价值。额外的“空”数组来自最后的print语句,而不是之前的那个。它们与提供的数组具有相同的长度但是为空的事实告诉您这种删除发生在原始数组上。

例如:

def mess_with_array(array):
    for i in range(2,len(array)):
        del array[2]

test = [1,2,3,4,5,6]
mess_with_array(test)
print test

结果为[1,2]。这是因为我们修改了原始数组。

诀窍是要么返回值(如果你不关心原始数组),要么使用副本并返回值。

更改

if len(newTotalArray) > 1:
    merge_sort(newTotalArray)

if len(newTotalArray) > 1:
    return merge_sort(newTotalArray)
else: return newTotalArray

并使用

运行该功能
arrayToSort = merge_sort(arrayToSort)

或者如果您希望使用副本并保留原始数组,请使用

进行调用
sortedArray = merge_sort(arrayToSort[:])

这个通过引用传递vs传递值的问题是微妙的,如果你不小心它会咬你很糟糕。在处理新语言时,我首先要寻找的是它的传递方式。

一个额外的提示(以及我如何弄清楚出了什么问题)。使用递归函数时,添加一个默认值为0的“调用”参数,并将其传递给每次递增1的函数,并在所有打印语句中提供它 - 这允许您查看每个调用中的哪些语句,并让你看到该功能在最后展开。