【问题标题】:Mergesort in Python, my first sorting algorithm, what went wrong?Python 中的 Mergesort,我的第一个排序算法,出了什么问题?
【发布时间】:2013-12-09 23:59:28
【问题描述】:
def mergesort(a):
    if len(a)<=1:
        return a

    else:
        mid=len(a)/2
        mergesort(a[:mid])
        mergesort(a[mid:])
        auxa=[]
        j=0
        k=mid
        while j<mid and k<len(a):
            if a[j]<a[k]:
                auxa.append(a[j])
                j+=1
            else:
                auxa.append(a[k])
                k+=1

        if j==mid:
            auxa.extend(a[k:])

        if k==len(a):
            auxa.extend(a[j:mid])

        a=auxa


        return a

testlist=[3,2,1]   
print mergesort(testlist) 

我得到的结果是 2 1 3

非常感谢任何帮助,谢谢!

【问题讨论】:

  • 不要忽略内部mergesort()调用的返回值

标签: python sorting merge


【解决方案1】:

您的函数 mergesort 返回一个新列表,并且不会像您期望的那样修改您提供的列表。因此,例如,当您调用 mergesort(a[:mid]) 时,您得到的是这些元素的新排序版本,而原来的 a[:mid] 保持完全相同。

编辑:这里的问题是 python 列表切片的工作方式。当你说a[:mid] 时,python 会创建一个原始的“副本”(我们不用担心副本的确切 type )。现在,当您在函数中修改此副本时,您所做的只是更改其中的引用以指向新的整数,而不是以任何方式修改原始整数。这里有一些代码来充实这一点:

def change(a):
    a[1] = 0

a = [1, 2, 3]
change(a)
a
>> [1, 0, 3]

a = [1, 2, 3]
change(a[:2])
a
>> [1, 2, 3] 

编辑 2:复制正确完成的值(如 cmets 中 (abamert) 所建议的那样):

def mergesort(a):
  if len(a)<=1:
    return a

  else:
    mid=len(a)/2
    a = mergesort(a[:mid]) + mergesort(a[mid:])
    auxa=[]
    j=0
    k=mid
    while j<mid and k<len(a):
        if a[j]<a[k]:
            auxa.append(a[j])
            j+=1
        else:
            auxa.append(a[k])
            k+=1

    if j==mid:
        auxa.extend(a[k:])

    if k==len(a):
        auxa.extend(a[j:mid])


    return auxa

这显然是更好的方法,涉及的复制更少,但我认为这个解决方案与原始代码的问题更相关。

【讨论】:

  • 很好的解释,但我想你也想提供解决方案。如果您想更改前半部分,只需执行a[:mid] = mergesort(a[:mid])。或者,可能更好的是,使用a = mergesort(a[:mid]) + mergesort(a[mid:]) 从两个已排序的部分中创建一个新的排序列表。
  • 我在 OP 代码中的任何地方都看不到 a[index] = value,也就是说,a 部分是否作为副本传递并不重要,a 无论如何都不会改变。 local_a = whatever 对外界没有影响,除非从函数中返回。但是 OP 忽略了来自 mergesort() 的返回值
  • 我想这是一个解释问题。您的观点是OP打算获取他的函数的返回值并使用它,但是忘记了。他有一个返回声明的事实支持了这一点。我的观点是,他预计传递的数组会发生变化。他试图做a = auxa 的事实支持了这一点。根据您更同意哪一个,问题要么是切片复制,要么是 OP 忽略返回值,争论哪个是“真实的”对我来说没有太大意义。
  • local_a = whatever .... 我当然同意。关键是 OP 可能并不明显它确实是本地 a。但是语义,真的。
【解决方案2】:

这是我想出的:

from collections import deque

def mergesort(array):
    if len(array) <= 1: return array

    midpoint = len(array) / 2

    left_array = deque(mergesort(array[:midpoint]))
    right_array = deque(mergesort(array[midpoint:]))

    merged_array = deque([])

    while len(left_array) and len(right_array):
        if left_array[0] < right_array[0]:
            merged_array.append(left_array.popleft())
        else:
            merged_array.append(right_array.popleft())

    merged_array.extend(left_array)
    merged_array.extend(right_array)

    return merged_array

print mergesort([3, 2, 1])

【讨论】:

  • pop(0) 是列表的O(n),它使您的算法二次而不是通常的O(n*log n) 用于合并排序。您可以尝试 collections.deque() 而不是 merged_array 列表。 while left_array and right_array: 按原样工作。在第一个循环之后,left_arrayright_array 为空。您可以使用merged_array.extend(left_array); merged_array.extend(right_array); 而不是最后两个循环。要支持 Python 3,您可以使用 //(地板除法)。
  • 谢谢!我没想到。
猜你喜欢
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
相关资源
最近更新 更多