Python算法100例-1.8 冒泡排序

完整源代码项目地址,关注博主私信’源代码’后可获取

1.问题描述

对N个整数(数据由键盘输入)进行升序排列。

2.问题分析

对于N个类型相同的数,我们可利用数组进行存储。冒泡排序是利用两个相邻元素之间进行比较交换的过程将一个无序表变成有序表。

冒泡排序的思想是:首先,从表头开始往后扫描数组,在扫描过程中逐对比较相邻两个元素的大小。若相邻两个元素中,前面的元素大于后面的元素,则将它们互换,称之为消去了一个逆序。在扫描过程中,不断地将两相邻元素中的大者往后移动,最后就将数组中的最大者换到了表的最后,这正是数组中最大元素应有的位置。然后,在剩下的数组元素中(n-1个元素)重复上面的过程,将次小元素放到倒数第2个位置。不断重复上述过程,直到剩下的数组元素为0为止,此时的数组就变为了有序。假设数组元素的个数为n,在最坏的情况下需要的比较总次数为:((n-1)+(n-2)+…+2+1)=n(n-1)/2。

3.算法设计

冒泡排序的过程我们可以用示意图简单地表示,如图所示。从整个排序过程中寻找规律,可知n个元素只需比较n-1次即可。假设一个数组中有7个元素,现在对这7个元素进行排序,只需比较6次即可得到所要的有序序列。示意图中最后加粗的数字即为经过一次交换位置固定的数字。

在这里插入图片描述

数组名用a表示,数组下标用j表示,则数组中相邻两个元素的下标可表示为a[j]、a[j+1]或a[j-1]、a[j]。在利用数组解决问题时需要注意数组下标不要越界,假如定义一个整形数组int a[n],则数组元素下标的取值范围是0~n-1,下标小于0或者大于n-1都视为下标越界。如果相邻元素采用a[j]、a[j+1]表示,则下标取值范围是0~n-2,如果采用a[j-1]、a[j]表示,下标取值范围则是1~n-1,所以读者在进行编程时一定要注数组下标越界的问题。

数组元素互换也是经常遇到的一类题型,一般这种情况下我们需要借助一个中间变量才可以完成,对于许多初学者来说经常犯的一个错误是对两个元素直接相互赋值,而不借助中间变量。我们先来看一个生活中的例子,在蓝墨水瓶中装有蓝墨水,红墨水瓶中装有红墨水,现在我们要把蓝墨水放到红墨水瓶中,红墨水放到蓝墨水瓶中。做法是先找一个白色空瓶(作用相当于程序中的中间变量),首先将蓝墨水倒入白色空瓶(t=a[i]或t=a[i+1]),接着将红墨水倒入蓝墨水瓶(a[i]=a[i+1]或a[i+1]=a[i]),最后将白瓶中的蓝墨水倒入红墨水瓶(a[i+1]=t或a[i]=t),经过这三步就完成了红墨水与蓝墨水的互换。如果不借助白色空瓶,直接把蓝墨水倒入红墨水瓶或把红墨水倒入蓝墨水瓶,这样必将破坏原来所存储的内容。第一次的交换过程可以用简单的程序段进行表示,代码如下:

for j in range(0, n-1):
    if a[j] > a[j+1]:
            t = a[j]                                    # 使用变量t暂存
            a[j] = a[j+1]
            a[j+1] = t

第二次的交换过程(最后一个元素经过第一轮比较已经确定,不需要再次进行比较)可表示为:

for j in range(0, n-2):
    if a[j] > a[j+1]:
        t = a[j]                                    # 使用变量t暂存
        a[j] = a[j + 1]
        a[j + 1] = t

第三次的交换过程(最后两个元素已经确定,不需要再次进行比较)可表示为:

for j in range(0, n-3):
    if a[j] > a[j+1]:
        t = a[j]                                    # 使用变量t暂存
        a[j] = a[j + 1]
        a[j + 1] = t

由上面的程序段可以发现,第一次比较的判定条件为j<n-1,第二次为j<n-2,第三次为j<n-3,以此类推,第i次的循环判定条件必为j<n-i。在编程过程中我们可以用两层循环来控制,第一层循环控制交换的轮数,第二层循环控制每轮需要交换的次数。

4.完整的程序

程序流程图如图所示。

在这里插入图片描述

根据上面的分析,编写程序如下:

%%time
# 冒泡排序
def bubbleSort(a):
    # 首先获取列表list的总长度,为之后循环比较做准备
    n = len(a)
    # 进行 n-1 次比较,控制比较的轮数
    i = 1
    while i <= n-1:
        # 控制每轮比较的次数
        j = 0
        while j < n-i:
            # 交换
            if a[j] > a[j+1]:
                t = a[j]
                a[j] = a[j+1]
                a[j+1] = t
            j += 1
        i += 1
    # 打印每一轮交换后的列表
    for a1 in a:
        print(a1, end=" ")

if __name__=="__main__":
    print("请为列表元素赋初值,列表末尾不能有空格:")
    x = input()
    a = x.split(" ")                                        # 以空格方式分割每个元素
    for i in range(0, len(a)):              # 输入多个值
        a[i] = int(a[i])
    print("你输入的列表元素为:\n", a)
    print("经过交换后的数组元素为:")
    bubbleSort(a)
请为列表元素赋初值,列表末尾不能有空格:
你输入的列表元素为:
 [5, 7, 9, 8, 2, 3, 1, 6, 4]
经过交换后的数组元素为:
1 2 3 4 5 6 7 8 9 CPU times: user 195 ms, sys: 53.3 ms, total: 248 ms
Wall time: 24.6 s

5.问题拓展

常用的排序方法除了上述的冒泡法外,还有选择排序、插入排序、快速排序、堆排序等,下面简单介绍选择排序。

选择排序的思想是:扫描整个线性表,第一轮比较拿数组中的第一个元素与其他元素进行比较,遇到比第一个元素小的元素则进行交换,再拿着交换之后的第一个元素接着从上次比较的位置与后面的元素进行比较,直到扫描到线性表的最后,从中选出最小的元素,将它交换到表的最前面(这是它应有的位置);第二轮比较的时候从第二个元素开始,依次与第三个、第四个直到最后一个进行比较,在比较过程中有比第二个元素小的元素则进行交换,接着与后面的元素比较;对剩下的子表采用同样的方法,直到子表为空。在最坏的情况下,需要比较n(n-1)/2次。

完整的程序如下:

%%time
# 选择排序
def selectionSort(a):
    # 求出列表的长度
    n = len(a)

    for i in range(0, n-1):
        for j in range(i+1, n):
            #交换
            if a[j] < a[i]:
                t = a[i]
                a[i] = a[j]
                a[j] = t
    for i in a:
        print(i, end=" ")

if __name__=="__main__":
    print("请为列表元素赋初值,列表末尾不能有空格:")
    x = input()
    a = x.split(" ")                                        # 以空格方式分割每个元素
    for i in range(0, len(a)):              # 输入多个值
        a[i] = int(a[i])
    print("你输入的列表元素为:\n", a)
    print("经过交换后的数组元素为:")
    selectionSort(a)
    print("\n")
请为列表元素赋初值,列表末尾不能有空格:
你输入的列表元素为:
 [5, 7, 9, 8, 2, 3, 1, 6, 4]
经过交换后的数组元素为:
1 2 3 4 5 6 7 8 9 

CPU times: user 124 ms, sys: 35.4 ms, total: 159 ms
Wall time: 15.5 s

不同排序法的效率是不同的,不同需求情况下可选择不同的方法。其他几种排序方法的原理有兴趣的读者可参阅数据结构的相关内容。

  • 38
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘逸高铁侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值