【问题标题】:Why is the range loop in bubble sort reversed?为什么冒泡排序中的范围循环会反转?
【发布时间】:2017-09-19 06:26:43
【问题描述】:

我是 Python 新手,正在学习 Python 中的数据结构。我正在尝试在 python 中实现冒泡排序算法,我做得很好,但我没有得到正确的结果。然后我找到了一些教程,在那里我看到他们首先设置了一个基本范围进行检查。

所以range在python中的语法是:

range([start], stop[, step])

而冒泡排序算法是:

def bubbleSort(alist):

   for i in range(len(alist) - 1, 0, -1):
       for j in range(i):

           if alist[j] > alist[j+1]:
               temp = alist[j]
               alist[j] = alist[j+1]
               alist[j+1] = temp

   return alist

print(bubbleSort([5, 1, 2, 3, 9, 8, 0]))

我理解算法的所有其他逻辑,但我无法理解为什么循环从列表末尾开始一直到列表的第一个元素:

for i in range(len(alist) - 1, 0, -1):

为什么这会反向遍历列表?这个循环的主要目的是设置范围条件,为什么我们不能像这样从第一个元素遍历到len(list) - 1

for i in range(0, len(alist) - 1, 1):

【问题讨论】:

  • 这是 Python 代码,显然是由认为他们仍在编写 C 代码的人编写的(for 循环是在 C for 循环之后建模的,尽管 python for 循环更灵活)。基本上,i 索引只是跟踪列表中未排序项目的数量。我建议查看其他冒泡排序实现以获得算法的更“pythonic”视图。
  • @CharlesAddis 除了交换部分,你会在那里改变什么?
  • @poke for 循环...外循环可以是一个带有标记值sorted的while循环...在python中实现冒泡排序的方法不止一种。请参阅此问题的第一个答案:stackoverflow.com/questions/895371/bubble-sort-homework
  • @CharlesAddis nothing 与 while 循环和布尔值有关,这使得其中任何一个都更具 Python 风格。我会争辩说,while 循环是该语言中最少的 Pythonic 结构。 – 在字面上 any 其他语言中使用布尔值同样可能,但您不希望这样做的原因正是这个问题的目的:避免在大约一半的时间内迭代已经排序的列表.
  • @poke 该链接并不是为了提供我所说的“完美”实现。在 python 中实现冒泡排序有多种方法。如果您使用enumerate,则根本不需要范围,并且在可迭代的长度上调用范围(或在可迭代上调用范围的变体)是针对 python 教条的一些最令人震惊的罪行......并且“避免在大约一半的时间内迭代已经排序的列表”......嘘。 “完全优化”的排序算法将考虑列表已经排序的情况if sorted(alist): return alist

标签: python sorting bubble-sort


【解决方案1】:

在您的代码中,索引i 是内部循环在交换元素时将考虑的最大索引。冒泡排序的工作方式是交换兄弟元素以将最大的元素向右移动。

这意味着在第一次外部迭代(或内部循环的第一个完整循环)之后,列表的最大元素位于列表的远端。所以它已经在正确的位置,不需要再次考虑。这就是为什么对于下一次迭代,i少一个跳过最后一个元素,只查看项目0..len(lst)-1

那么在下一次迭代中,最后两个元素会正确排序,所以只需要查看0..len(lst)-2这个项目,以此类推。

所以你想递减i,因为列表末尾越来越多的元素已经在正确的位置,不需要再查看了。你没有必须这样做;你也可以总是让内部循环一直到最后,但你不需要,所以你可以跳过一些迭代而不这样做。


我问为什么我们要在 len(list)-1,0 这样的列表中反转。为什么我们不像0,len(list)-1那样前进?

我希望上面的解释已经涵盖了这一点,但让我们详细介绍一下。尝试在外循环末尾添加print(i, alist)。所以你得到i的每次迭代的结果:

>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
6 [1, 3, 5, 2, 8, 0, 9]
5 [1, 3, 2, 5, 0, 8, 9]
4 [1, 2, 3, 0, 5, 8, 9]
3 [1, 2, 0, 3, 5, 8, 9]
2 [1, 0, 2, 3, 5, 8, 9]
1 [0, 1, 2, 3, 5, 8, 9]

如您所见,列表将从右到左排序。这对我们的索引i 很有效,这将限制内部循环的距离:例如,对于i = 4,我们最后已经有 3 个已排序的元素,因此内部循环只需查看前 4 个元素。

现在,让我们尝试将range 更改为另一个方向。循环将是for i in range(0, len(alist))。然后我们得到这个结果:

>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
0 [5, 1, 3, 9, 2, 8, 0]
1 [1, 5, 3, 9, 2, 8, 0]
2 [1, 3, 5, 9, 2, 8, 0]
3 [1, 3, 5, 9, 2, 8, 0]
4 [1, 3, 5, 2, 9, 8, 0]
5 [1, 3, 2, 5, 8, 9, 0]
6 [1, 2, 3, 5, 8, 0, 9]

如您所见,这根本没有排序。但为什么? i 仍然限制了内部循环可以走多远,所以在i = 1,循环只会查看第一对并对其进行排序;其余的将保持不变。在i = 2,循环将查看前两对并交换它们(一次!);其余的将保持不变。等等。当内部循环可以到达最后一个元素(仅在最后一次迭代中)时,没有足够的迭代将零(也恰好是最小的元素)交换到最左边。

这又是因为冒泡排序的工作原理是首先将最大的元素排序到最右边。所以我们必须通过使内部循环能够完全到达右侧来启动算法。只有当我们确定这些元素处于正确的位置时,我们才能停止走那么远。

一种方法可以使用递增的外循环:首先对最小的元素进行排序。但这也意味着我们必须在最右侧开始内部循环,以确保在查找最小元素时检查所有元素。所以我们真的必须让这些循环朝着相反的方向前进。

【讨论】:

  • 太棒了,现在更清楚了,但我还有一些疑问,没有完全清除,为什么我们实际上需要内循环,这是最后一个问题。
  • 内部循环负责找到 一个 大元素并将其排序到列表的末尾(通过交换两个相邻元素)。外部循环负责重复该过程足够多次,以确保最终对所有元素进行排序。如果您在理解它时遇到困难,请在纸上亲手尝试一下,您肯定会看到它现在到底是如何工作的。分步示例和动画on Wikipedia 也确实有助于解释该过程。
【解决方案2】:

这是因为当您从列表的开头冒泡到末尾时,最终结果是列表中的最后一项将被排序(您已将最大的项冒泡到末尾)。因此,当您进行下一个气泡时,您不想在列表中包含最后一项(您知道它已经在正确的位置)。这意味着您需要排序的列表会变得更短,从末尾开始,一直向下。在这段代码中,i 始终是剩余未排序列表的长度。

【讨论】:

  • 您的意思是“最终结果是列表中的最后一项将是“未”排序的?您写的是“排序”?
  • 我的意思是“排序”。如果你有 [5, 4, 6, 2, 0],那么在一个气泡之后你将有 [5, 4, 2, 0, 6] 并且最后一项“6”被排序。在另一个气泡之后,您有 [4, 2, 0, 5, 6] 并且最后两个已排序,依此类推。
【解决方案3】:

您可以将其用于:

for i in range(0,len(alist)-1,1):

但因此您应该更改您的第二次迭代:

for j in range(0,len(alist)-i,1):

我认为在第一行使用反向迭代的目的是为了简化第二次迭代。这就是使用python的好处

作为@Jeremy McGibbon 的回答,冒泡排序背后的逻辑是避免j 到达列表后面的“排序部分”。使用示例代码时,j 的范围将随着i 的值的减小而减小。当您将i 更改为增加时,您应该以不同方式处理j 迭代

【讨论】:

    【解决方案4】:

    您可以编写如下代码

    lst = [9,6,5,7,8,3,2,1,0,4]
    lengthOfArray = len(lst) - 1
    
    for i in range(lengthOfArray):
        for j in range(lengthOfArray - i):
            if lst[j] > lst[j + 1]:
                lst[j], lst[j + 1] = lst[j + 1], lst[j]
    
    print(lst)
    

    【讨论】:

      猜你喜欢
      • 2021-09-26
      • 2022-01-05
      • 2012-08-28
      • 2015-09-12
      • 2015-10-18
      • 2021-05-11
      • 2016-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多