Python中的冒泡排序算法
冒泡排序是最直接的排序算法之一。它的名字来自于算法的工作方式:每经过一个新遍历,列表中最大的元素就会向正确的位置“冒泡”。
冒泡排序包括对列表进行多次遍历、逐个比较元素以及交换顺序混乱的相邻项。
在Python中实现冒泡排序
下面是一个用Python实现的冒泡排序算法:
由于此实现按升序对数组进行排序,因此每个步骤都“冒泡”到数组末尾的最大元素。这意味着每个迭代比前一个迭代的步骤更少,因为数组中连续的更大的部分被排序。
第4行和第10行中的循环决定了算法在列表中运行的方式。注意j最初是如何从列表中的第一个元素到紧接前一个元素的。在第二次迭代中,j运行到最后两个项目,然后运行到最后三个项目,以此类推。在每次迭代结束时,列表的结束部分将被排序。
随着循环的进行,第15行比较每个元素与其相邻的值,如果它们的顺序不正确,第18行交换它们。这确保在函数的末尾有一个排序的列表。
注意:上面代码的第13、23和27行中的already_sort标志是对算法的优化,在一个全功能的冒泡排序实现中并不需要它。但是,如果列表在循环结束之前就完全排序完毕,它允许函数节省不必要的步骤。
作为练习,您可以删除此标志的使用,并比较两个实现的运行时。
要正确地分析算法的工作方式,请考虑一个包含值[8,2,6,4,5]的列表。假设您正在从上面使用bubble_sort()。下面的图展示了每次迭代算法时数组的样子:
冒泡排序过程
现在一步一步地看看随着算法的发展,数组发生了什么:
代码首先比较第一个元素8和它的相邻元素2。因为8 > 2,值被交换,结果是以下顺序:[2,8,6,4,5]。然后,该算法将第二个元素8与它的相邻元素6进行比较。由于8 > 6,值被交换,结果是以下顺序:[2,6,8,4,5]。接下来,算法比较第三个元素8和它的相邻元素4。因为8 > 4,它也交换值,结果是以下顺序:[2,6,4,8,5]。最后,算法比较第四个元素8和它的相邻元素5,并交换它们,结果是[2,6,4,5,8]。此时,算法完成了对列表的第一次遍历(i = 0),注意值8是如何从初始位置冒泡到列表末尾的正确位置的。第二次遍历(i = 1)考虑到列表的最后一个元素已经定位,并将重点放在剩下的四个元素[2、6、4、5]上。在这一遍的末尾,值6找到了它的正确位置。第三遍遍历列表,将值5定位,以此类推,直到对列表进行排序。
测量冒泡排序的运行时复杂度
冒泡排序的实现由两个嵌套的for循环组成,算法在其中执行n - 1比较,然后执行n - 2比较,以此类推,直到完成最后的比较。这总共(n - 1) + (n - 2) + (n - 3) +…+ 2 + 1 = n (n - 1) / 2比较,也可以写成n2 -n。
您在前面了解到,Big O关注的是运行时相对于输入的大小是如何增长的。这意味着,为了把上面的方程变成算法的大O复杂度,你需要去掉常数,因为它们不会随着输入的大小而改变。
这样做可以简化符号为n2 - n。由于n2的增长速度比n快得多,最后一项也可以去掉,使得冒泡排序的平均和最坏情况复杂度都是O(n2)。
在算法接收到一个已经排序的数组的情况下(假设实现包括前面解释过的already_ordered标志优化),运行时复杂度将下降到一个更好的O(n),因为算法不需要访问任何元素超过一次。
那么O(n)就是冒泡排序的最佳情况下的运行时复杂度。但是请记住,最好的情况是一个例外,在比较不同的算法时,您应该关注平均情况。
为冒泡排序实现计时
使用本教程前面的run_sorting_algorithm(),以下是冒泡排序处理包含一万项的数组所需的时间。第8行替换了算法的名称,其他内容保持不变:
现在可以运行脚本来获得bubble_sort的执行时间:
>>>python sorting.py
Algorithm: bubble_sort. Minimum execution time: 73.21720498399998
用1万个元素对数组排序花费了73秒。这表示run_sorting_algorithm()运行的10次重复中最快的一次执行。多次执行此脚本将产生类似的结果。
注意:冒泡排序的一次执行花费了73秒,但是算法使用这个时间运行了10次。这意味着,假设您有类似的硬件特征,那么您的代码运行大约需要73 * 10 = 730秒。较慢的机器可能需要更长的时间来完成。
分析了冒泡排序的优缺点
冒泡排序算法的主要优点是简单。它的实现和理解都很简单。这可能是大多数计算机科学课程引入使用冒泡排序的主题的主要原因。
如前所述,冒泡排序的缺点是速度慢,运行时复杂度为O(n2)。不幸的是,这将它排除在对大型数组排序的实际候选项之外。
补充:排序算法的重要性
排序算法是计算机科学中研究最深入的算法之一。您可以使用许多不同的排序实现和应用程序来使代码更高效。
您可以使用排序来解决各种问题:
搜索:如果对列表进行排序,则搜索列表中的项目会更快。选择:使用排序后的数据,可以根据列表与其余项目的关系从列表中选择项目。例如,当值以升序或降序排列时,找到第k 个最大或最小值,或找到列表的中值会容易得多。重复项:对列表进行排序后,可以非常快速地找到列表中的重复值。分布:如果对列表进行排序,则分析列表中项目的频率分布非常快。例如,使用排序列表查找最频繁或最少出现的元素相对简单。