赞
踩
一、冒泡排序属于经典算法,基本入门的时候都会手动编写冒泡排序,其算法思想为(以升序为例):
比较相邻的元素。如果第一个比第二个大,交换位置;
然后比较第二个和第三个元素,如果第二个比第三个大,交换位置。以此类推,对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,此时得到最后的元素是最大的数;
一趟比较完之后,针对前面(1, n-1)个元素重复以上的步骤,最后一个元素已经排好序。
依次再针对(1,n-2 ),(1,n-3 )......(1, 2)中的元素重复上面的步骤,直到没有任何一对数字需要比较,得到升序排列
其算法代码实现如下:
- def bubble_sort_1(list_sort):
- for i in range(0, len(list_sort)):
- for j in range(0, len(list_sort) - i -1):
- if list_sort[j] > list_sort[j + 1]:
- list_sort[j], list_sort[j+1] = list_sort[j+1], list_sort[j]
- i = i + 1
- print("遍历次数为:%d" % i)
- return list_sort
-
- print(bubble_sort([5, 8, 6, 3, 9, 2, 1, 7]))
我们对[5, 8, 6, 3, 9, 2, 1, 7]进行排序,最后输出结果为[1, 2, 3, 5, 6, 7, 8 ,9], 外层的循环要遍历n次,每次遍历过程中的元素和元素的总数n相当,因此该算法的时间复杂度为O(n^2)。
二、在实际的排序情况中,会存在一些特殊的情况,比如我们对[3, 4, 2, 1, 5, 6, 7, 8]进行排序,按照我们上面的算法,第一次遍历后,序列为[3, 2, 1, 4, 5, 6, 7, 8],第二次遍历后序列为[2, 1, 3, 4, 5, 6, 7, 8] ,第三次遍历后[1, 2, 3, 4, 5, 6, 7, 8] ,按照实际情况现在的序列已经有序,不需要继续遍历比较,,但在上面的算法中还会对他们继续进行比较操作,直到n次遍历完为止。针对这种情况,我们可以设置一个交换标记,当在一次遍历过程中,没有发生元素的位置交换,说明这时候序列已经有序,则算法可以提前终止,加入元素交换标识符change, 用代码实现算法改进:
- def bubble_sort_2(list_sort):
- for i in range(0, len(list_sort)):
- change = 0
- for j in range(0, len(list_sort) - i -1):
- if list_sort[j] > list_sort[j + 1]:
- list_sort[j], list_sort[j+1] = list_sort[j+1], list_sort[j]
- change = 1
- if change == 0:
- break
- i = i + 1
- print("遍历次数为:%d" % i)
- return list_sort
-
- print(bubble_sort_2([5, 8, 6, 3, 9, 2, 1, 7]))
'运行
两个版本对相同的数列进行比较,外层的遍历次数分别为8次和6次。
- 遍历次数为:8
- [1, 2, 3, 5, 6, 7, 8, 9]
- 遍历次数为:6
- [1, 2, 3, 5, 6, 7, 8, 9]
'运行
每一次遍历后的序列如下输出所示:
- [5, 6, 3, 8, 2, 1, 7, 9]
- [5, 3, 6, 2, 1, 7, 8, 9]
- [3, 5, 2, 1, 6, 7, 8, 9]
- [3, 2, 1, 5, 6, 7, 8, 9]
- [2, 1, 3, 5, 6, 7, 8, 9]
- [1, 2, 3, 5, 6, 7, 8, 9]
- [1, 2, 3, 5, 6, 7, 8, 9]
- [1, 2, 3, 5, 6, 7, 8, 9]
- 遍历次数为:8
- [5, 6, 3, 8, 2, 1, 7, 9]
- [5, 3, 6, 2, 1, 7, 8, 9]
- [3, 5, 2, 1, 6, 7, 8, 9]
- [3, 2, 1, 5, 6, 7, 8, 9]
- [2, 1, 3, 5, 6, 7, 8, 9]
- [1, 2, 3, 5, 6, 7, 8, 9]
- 遍历次数为:6
'运行
从每次遍历详细的序列输出可以看到,用第一种方法遍历到第6次后,序列已经为有序,但是遍历还会继续直到8次结束,而对于加入标识符改进的第二种方法,发现在第6次已经没有进行元素交换后,遍历立即停止,不再继续进行。
三、对于某些序列我们可以做进一步优化,比如对于序列[3, 4, 2, 1, 5, 6, 7, 8],后半部分[5, 6, 7, 8]已经排序好,我们先用前两种方法进行排序,可以看到输出结果:
- [3, 2, 1, 4, 5, 6, 7, 8]
- [2, 1, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- 遍历次数为:8
- [3, 2, 1, 4, 5, 6, 7, 8]
- [2, 1, 3, 4, 5, 6, 7, 8]
- [1, 2, 3, 4, 5, 6, 7, 8]
- 遍历次数为:3
'运行
第一种方法依然遍历了8次,第二种方法只遍历了3次就提前结束了。但是在每次遍历的过程种,虽然[5, 6, 7, 8]已经有序了,但是还依然需要参与相邻元素的两两比较,对于这种情况我们可以找到序列有序区的边界,在每一次遍历的内部比较和元素交换过程中,最后一次元素交换的位置的右边是有序的元素序列,对于已经有序的序列,我们在每次遍历的过程中不必再去做比较操作,加入边界记录符sorted_border,内层循环 j 的迭代边界由len(list_sort) - i -1改为sorted_border,算法改进如下:
- def bubble_sort_3(list_sort):
- print("-"*120)
- last_change_index = 0
- sorted_border = len(list_sort) - 1
- for i in range(0, len(list_sort)):
- change = 0
- for j in range(0, sorted_border):
- if list_sort[j] > list_sort[j + 1]:
- list_sort[j], list_sort[j+1] = list_sort[j+1], list_sort[j]
- last_change_index = j
- change = 1
- print("j的值为:%d" % j, end=" ")
- sorted_border = last_change_index
- if change == 0:
- break
- i = i + 1
- print(list_sort)
- print("结束")
- return list_sort
-
- bubble_sort_3([3, 4, 2, 1, 5, 6, 7, 8])
'运行
用第二种方法排序此数列和第三种方法进行比较,输出结果如下:
- j的值为:0 j的值为:1 j的值为:2 j的值为:3 j的值为:4 j的值为:5 j的值为:6 [3, 2, 1, 4, 5, 6, 7, 8]
- j的值为:0 j的值为:1 j的值为:2 j的值为:3 j的值为:4 j的值为:5 [2, 1, 3, 4, 5, 6, 7, 8]
- j的值为:0 j的值为:1 j的值为:2 j的值为:3 j的值为:4 [1, 2, 3, 4, 5, 6, 7, 8]
- j的值为:0 j的值为:1 j的值为:2 j的值为:3 结束
- ------------------------------------------------------------------------------------------------------------------------
- j的值为:0 j的值为:1 j的值为:2 j的值为:3 j的值为:4 j的值为:5 j的值为:6 [3, 2, 1, 4, 5, 6, 7, 8]
- j的值为:0 j的值为:1 [2, 1, 3, 4, 5, 6, 7, 8]
- j的值为:0 [1, 2, 3, 4, 5, 6, 7, 8]
- 结束
从输出结果可以看出,第一次遍历过程种元素比较的次数相同,j 的值都为6,结束第一趟遍历后,第三种方法由于记录了最后一个交换元素的索引,即索引为2的元素值和它后面紧邻索引为3的元素值4进行交换,[5, 6, 7, 8]已经有序了并没有进行交换操作。因此,在第二次的遍历过程种,第二种算法 j 的值为5,而第三种算法种 j 的值减小到1就结束比较操作了,后面的两趟遍历中,第三种算法比较的次数明显少于第二种算法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。