基本算法——冒泡排序(Python版)


活动地址:CSDN21天学习挑战赛

1.冒泡排序(Bubble Sort)

1.1 概念:

冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,因为每一个元素都可以像小气泡一样,根据自身大小一点一点向数组的一侧移动。

1.2 基本原理:

每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。

而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。

2.图示

在这里插入图片描述

3.代码实现(python):

def bubble_sort(l):
    for i in range(len(lst)): # 外循环的趟数
        for j in range(len(lst) - 1):
            if l[j] > l[j + 1]:
                l[j],l[j+1]=l[j+1],l[j]
    print(l)


lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)

优化点一:

其实我们可以看到上面的代码有可以优化的地方,每一趟外循环的时候,内循环比较包括的交换的次数是相等的,但是其实这是完全没必要的,因为每执行一次外循环之后,都会有一个极值通过冒泡变成了有序的,所以我们这个时候只需要找一下规律,看一下每一次内循环的次数应该是多少,应该是:

元素数 - 外循环的当前值 - 1

这个时候上面的元素可以优化为:

# 优化版一
def bubble_sort(l):
    for i in range(len(lst)):  # 外循环的趟数
        for j in range(len(lst) - i - 1):
            if l[j] > l[j + 1]:
                l[j], l[j + 1] = l[j + 1], l[j]
    print(l)


lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)

优化点二:

但其实这个算法还是有可以优化的地方,比如我们在执行了外循环的中的几趟,这个时候元素已经就全局有序了,并不需要再进行后续外循环的执行了,这个时候我们可以认为在一趟的外循环中如果一次交换都没有,那么我们就可以认为这个列表全局有序了,这个时候就可以跳出外循环,我们使用一个标记进行记录内循环中是否有元素进行交换,可以得到下面的代码:

# 优化版二
def bubble_sort(l):
    for i in range(len(lst)):  # 外循环的趟数
        is_sorted = True
        for j in range(len(lst) - i - 1):
            if l[j] > l[j + 1]:
                is_sorted = False # 有元素交换,所以不是有序,标记变为False
                l[j], l[j + 1] = l[j + 1], l[j]

        if is_sorted: # 一趟下来是否发生位置交换,如果没有交换直接跳出外循环
            break
    print(l)


lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)

优化点三:

还有一个问题。那如果数列中前半部分是无序的,后半部分是有序的呢?比如(3,4,2,1,5,6,7,8)这个数组,其实后面的许多元素已经是有序的了,但是每一轮还是白白比较了许多次呢?

如果按冒泡排序代码原始版来分析的话,有序区的长度和排序的轮数是相等的。比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 ……。
但是呢,实际数列真正的有序区可能会大于这个长度,也就是你上面这个例子,第二轮中后面 5 个实际上都已经属于有序区了。因此后面的比较是没有意义的了。
我们可以这样做来避免这种情况:在每一轮排序的最后,记录一下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区了。

# 优化版三
def bubble_sort(l):
    for i in range(len(lst)):  # 外循环的趟数
        is_sorted = True
        # 无序数列的边界,每次比较只需要比到这里为止
        unsorted_border = (len(lst) - i - 1) if i == 0 else unsorted_border
        for j in range(unsorted_border):
            if l[j] > l[j + 1]:
                print(l)
                is_sorted = False  # 有元素交换,所以不是有序,标记变为False
                l[j], l[j + 1] = l[j + 1], l[j]
                lastIndex = j
        unsorted_border = lastIndex
        if is_sorted:
            break


lst = [3, 4, 2, 1, 5, 6, 7, 8]
bubble_sort(lst)

4.算法性能

4.1 时间复杂度:

由上图可知,4 个石子的时候排完序需要 3 趟,第一趟需要比较3次,第二趟需要比较2次,第三趟需要比较1次,那一共比较了 3 + 2 + 1 次;

那如果有 n 个石子呢?

那就需要 (n-1) + (n-2) +…+2+1 次,这不就是一个等差数列吗,很显然:
基本算法——冒泡排序(Python版)_第1张图片

你可能感兴趣的:(算法,python,算法,排序算法)