【问题标题】:Trouble tracing my Merge sort algorithm (Python 3)无法跟踪我的合并排序算法(Python 3)
【发布时间】:2020-06-30 20:49:18
【问题描述】:

我在 Python 3 中编写了一个简短的合并排序算法。我很难理解它是如何实现正确结果的,因为当我尝试跟踪它的逻辑步骤时,我最终会得到一个乱序列表。注释代码如下所示。

我具体指的是代码的合并部分。三个“while”循环。

让我用一个例子来说明什么让我感到困惑。我在注释中解释了细节。

提前感谢您的时间和帮助。

假设我们要合并两个数组。

left = [2,6]
right = [4,8]

def merge_sort(array):

    if len(array) > 1:

        middle = len(array)//2

        left = array[:middle]
        right = array[middle:]

        merge_sort(left)
        merge_sort(right)

        i = j = k = 0

        while i < len(left) and j < len(right):

            # i.e. if 2 < 4
            if left[i] < right[j]: 

                # The first index of the array is assigned the value 2
                array[k] = left[i]  

                # i = 1 now
                i += 1

            # The 'else' statement is not executed, because the 'if' statement was.
            else:

                array[k] = right[j]
                j += 1

            # k = 1 now
            k += 1 

        # The 'while' loop below assigns '6' as the value for index 1 of the array and terminates.
        # k = 2 now
        while i < len(left):
       
            array[k] = left[i]
            i += 1
            k += 1

        # The last 'while' loop assigns '4' and '8' to indexes 2 and 3 of the array, respectively.
        while j < len(right):

            array[k] = right[j]
            j += 1
            k += 1

        # The algorithm terminates and from what I can see I should end up with the array of [2,6,4,8].
        # I do not, however. It is sorted in ascending order and I cannot see where I'm making a mistake.

【问题讨论】:

  • 当你只传入一个列表时,这如何合并两个列表?您的示例与代码的编写方式不一致。

标签: python python-3.x algorithm sorting mergesort


【解决方案1】:

首先,请注意您的措辞,要明确合并排序不是合并不同的数组,合并排序巧妙地将单个未排序的数组解构为子数组(在我们的例子中是左和右)并将它们单独排序并将它们合并回来再次进入单个数组并进行最终排序。换句话说,你给这个函数传递了一个未排序的数组,它返回一个排序好的数组。如果你需要合并两个数组,你会在调用它之前这样做。

合并排序

“归并排序是一种递归算法,它不断地将一个列表分成两半。如果列表为空或有一项,则按定义排序(基本情况)。如果列表多于一项,我们拆分列表并递归地在两半上调用合并排序。一旦对两半进行排序,就会执行称为合并的基本操作。合并是采用两个较小的排序列表并将它们组合成一个单独的排序的过程,新列表。”

调试/分析代码

为了帮助理解它的工作原理(和调试),至少注入打印 cmets 以最好地更详细地显示正在发生的事情。我已经采用了您编写的内容并添加了 print cmets 并向函数传递了一个字符串,以帮助确定它正在排序的数组(左或右)。您可以看到拆分排序和合并,因为它通过将数组拆分为大小为一并在此过程中合并排序的子数组等来完成排序...

def merge_sort(array,type):
    print('merge_sort =>' + type)
    if len(array) < 2:
        print('Array < 2 nothing changed')
        return array
    middle = len(array) // 2
    left = array[:middle]
    right = array[middle:]
    print('splitting : ' + str(array))
    merge_sort(left,'left')
    merge_sort(right,'right')
    i = j = k = 0
    print('sorting.. Left/Right:' + str(left) + str(right))
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            print(' - left[i] < right[j] ('+ str(left[i]) + ' < ' + str(right[j]) + ') set array[' + str(k) + '] = ' + str(left[i]) + '')
            array[k] = left[i]
            i += 1
        else:
            print(' - else left[i] >= right[j] ('+ str(left[i]) + ' >= ' + str(right[j]) + ') set array[' + str(k) + '] = ' + str(right[j]) + '')
            array[k] = right[j]
            j += 1
        k += 1
    while i < len(left):
        print(' - WHILE i < len(left), ('+str(i) +' < '+str(len(left))+'), set array[' + str(k) + '] = ' + str(left[i]) + '')
        array[k] = left[i]
        i += 1
        k += 1
    while j < len(right):
        print(' - while j < len(right) ('+str(j) +' < ' + str(len(right)) + '), set array[' + str(k) + '] = ' + str(right[j]) + '')
        array[k] = right[j]
        j += 1
        k += 1
    print("returning.." + str(array))
    return array

arr = [2,6,4,8]
result = merge_sort(arr,'full')
print(result)

提供以下输出:

merge_sort =>full
splitting : [2, 6, 4, 8]
merge_sort =>left
splitting : [2, 6]
merge_sort =>left
Array < 2 nothing changed
merge_sort =>right
Array < 2 nothing changed
sorting.. Left/Right:[2][6]
 - left[i] < right[j] (2 < 6) set array[0] = 2
 - while j < len(right) (0 < 1), set array[1] = 6
returning..[2, 6]
merge_sort =>right
splitting : [4, 8]
merge_sort =>left
Array < 2 nothing changed
merge_sort =>right
Array < 2 nothing changed
sorting.. Left/Right:[4][8]
 - left[i] < right[j] (4 < 8) set array[0] = 4
 - while j < len(right) (0 < 1), set array[1] = 8
returning..[4, 8]
sorting.. Left/Right:[2, 6][4, 8]
 - left[i] < right[j] (2 < 4) set array[0] = 2
 - else left[i] >= right[j] (6 >= 4) set array[1] = 4
 - left[i] < right[j] (6 < 8) set array[2] = 6
 - while j < len(right) (1 < 2), set array[3] = 8
returning..[2, 4, 6, 8]

这会产生一些东西。像这样:

参考资料: How do I merge arrays in python? https://runestone.academy/runestone/books/published/pythonds/SortSearch/TheMergeSort.html

【讨论】:

  • 感谢您提供详尽的解释以及您投入的时间!我确实对那里发生的事情有了更好的了解:)
  • @DeadManProp 谢谢,但我觉得我有最好的答案哈哈(我错了哈哈)
  • 对不起,我不是故意让你觉得被低估了。我用复选标记标记了其他响应,因为它指出了在执行代码时我在跟踪步骤时出错的确切“时刻”。尽管如此,您的回答让我更好地理解了我的代码,如果可能的话,我肯定会给出两个复选标记。遗憾的是系统不允许我这样做。
【解决方案2】:

在您的注释中,您似乎过早地退出了第一个 while 循环,当代码实际运行 3 次时,您在一次运行后停止。以下是您将如何关注 wgat 实际发生的情况:

  • 你运行一次,然后你有k=1i=1j=0
  • 所以你再次遍历这个循环(这次是else被执行,并将4分配给数组的索引1,现在k=2i=1j=1
  • 所以你第三次运行循环,执行if,最后执行k=3i=2j=1,所以你退出了第一个while

【讨论】:

  • 非常感谢!这正是我所缺少的。现在我想起来似乎很清楚,但不知何故我忘记了当 i = 1 时第一个“while”循环应该继续。
猜你喜欢
  • 2020-08-23
  • 2021-06-27
  • 1970-01-01
  • 2017-05-12
  • 2014-03-31
  • 2013-08-04
  • 2016-03-11
  • 2020-12-07
  • 1970-01-01
相关资源
最近更新 更多