基于比较的排序算法之 冒泡, 插入, 选择

冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是 O(n^2),比较高,适合小规模数据的排序。

如何分析"排序算法"

排序算法的执行效率
1.最好情况, 最坏情况, 平均情况时间复杂度

(1)分别给出最好情况、最坏情况、平均情况下的时间复杂度

(2)最好、最坏时间复杂度对应的要排序的原始数据是什么样的.

2.时间复杂度的系数、常数 、低阶

我们知道,时间复杂度反应的是数据规模 n 很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶。

但是实际的软件开发中,我们排序的可能是 10 个、100 个、1000 个这样规模很小的数据,所以,在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来

3. 比较次数和交换(或移动)次数

基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。

排序算法的内存消耗

(1) 空间复杂度

(2) 原地排序: 原地排序算法,就是特指空间复杂度是O(1)的排序算法

排序算法的稳定性

这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。

举例:

比如我们有一组数据 2,9,3,4,8,3,按照大小排序之后就是 2,3,3,4,8,9。这组数据里有两个 3。

经过某种排序算法排序之后,如果两个 3 的前后顺序没有改变,那我们就把这种排序算法叫作稳定的排序算法

如果前后顺序发生变化,那对应的排序算法就叫作不稳定的排序算法

实际应用

真正软件开发中,我们要排序的往往不是单纯的整数,而是一组对象,我们需要按照对象的某个 key 来排序

比如说,我们现在要给电商交易系统中的“订单”排序。订单有两个属性,一个是下单时间,另一个是订单金额。如果我们现在有 10 万条订单数据,我们希望按照金额从小到大对订单数据排序。对于金额相同的订单,我们希望按照下单时间从早到晚有序。对于这样一个排序需求,我们怎么来做呢?

借助稳定排序算法,解决思路是这样的:我们先按照下单时间给订单排序,注意是按照下单时间,不是金额。排序完成之后,我们用稳定排序算法,按照订单金额重新排序。两遍排序之后,我们得到的订单数据就是按照金额从小到大排序,金额相同的订单按照下单时间从早到晚排序的。

第一次排序之后,所有的订单按照下单时间从早到晚有序了。在第二次排序中,我们用的是稳定的排序算法,所以经过第二次排序之后,相同金额的订单仍然保持下单时间从早到晚有序。

冒泡排序(Bubble Sort)

冒泡排序只会操作相邻的两个数据

优化: 当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作

第一次冒泡的操作:

在这里插入图片描述

python实现

# 冒泡排序, a 表示数组, n 表示数组大小
def bubbleSort(a):
    n = len(a)
    if n <= 1:
        return a
    for i in range(n):
        # 提前退出冒泡循环的标志位
        flag = False
        for j in range(n-i-1): # 从0开始  n-(i+1) 6-(0+1)=5次比较,即j:0-4
            if a[j] > a[j+1]: # 交换
                a[j], a[j+1] = a[j+1], a[j]
                flag = True # 表示有数据交换

        if not flag: # 没有数据交换, 提前退出
            break
    return a

res = bubbleSort([2,9,8,6,4,5,1,7,3])
print(res) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

三个问题:

  • 1.冒泡排序是原地排序算法吗?

冒泡的过程只涉及相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为 O(1),是一个原地排序算法。

  • 2.冒泡排序是稳定的排序算法吗?

在冒泡排序中,只有交换才可以改变两个元素的前后顺序。为了保证冒泡排序算法的稳定性,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法。

  • 3.冒泡排序的时间复杂度是多少?

最好情况下,要排序的数据已经是有序的了,我们只需要进行一次冒泡操作,就可以结束了,所以最好情况时间复杂度是 O(n)。例如 :1,2,3,4,5,6

最坏的情况是,要排序的数据刚好是倒序排列的,我们需要进行 n 次冒泡操作,所以最坏情况时间复杂度为 O(n^2)。例如: 6,5,4,3,2,1

平均时间复杂度就是加权平均期望时间复杂度: 如果用概率论方法定量分析平均时间复杂度,涉及的数学推理和计算就会很复杂。另一种思路,通过“有序度”和“逆序度”这两个概念来进行分析。O(n^2)。

我们排序的过程就是一种增加有序度,减少逆序度的过程,最后达到满有序度,就说明排序完成了。

插入排序(Insertion Sort)

将数组中的数据分为两个区间,已排序区间未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。

插入排序也包含两种操作,一种是元素的比较,一种是元素的移动

对于不同的查找插入点方法(从头到尾、从尾到头),元素的比较次数是有区别的。但对于一个给定的初始序列,移动操作的次数总是固定的,就等于逆序度。

img

python实现

# 插入排序 , a表示数组,n表示数组大小
def insertionSort(a):
    n = len(a)
    if n <= 1:
        return
    for i in range(1,n):
        value = a[i] # 未排序部分的首元素的值赋给value
        j = i-1 # 排序部分的末元素
        while j >= 0: # 排序部分的元素从尾到头遍历
            if a[j] > value: # 如果 value的值小于排序部分的元素a[j]
                a[j+1] = a[j] # 数据移动 讲a[j]往后移动,则"空"出来一个位置
            else:
                break
            j -= 1
        a[j+1] = value # 插入数据
        # 将value值(即原来未排序部分的首元素的值)放到a[j+1]的位置
        # 此时 j要么=0 , 要么比较后a[j] <= value无法再移动数据

    return a

res = insertionSort([4,5,6,1,3,2])
print(res) # [1, 2, 3, 4, 5, 6]

三个问题:

  • 1.插入排序是原地排序算法吗?

并不需要额外的存储空间,所以空间复杂度是 O(1),也就是说,这是一个原地排序算法。

  • 2.插入排序是稳定的排序算法吗?

  • 3.插入排序的时间复杂度是多少?

最好情况时间复杂度为 O(n)。注意,这里是从尾到头遍历已经有序的数据。
最坏情况时间复杂度为 O(n^2)
(在数组中插入一个数据的平均时间复杂度是 O(n)。)
所以,对于插入排序来说,每次插入操作都相当于在数组中插入一个数据,循环执行 n 次插入操作,所以平均时间复杂度为 O(n^2)。

选择排序(Selection Sort)

已排序区间未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

在这里插入图片描述

python实现

def selectionSort(a):
    length = len(a)
    if length <= 1:
        return

    for i in range(length): # i索引之前的区间为已排序区间, i索引及之后的区间为未排序区间
        min_index = i # 赋初值
        min_val = a[i]
        for j in range(i, length): # 遍历未排序区间
            if a[j] < min_val:
                min_val = a[j] # min_val存储最小的元素
                min_index = j # min_index 存储最小的元素对应的索引
        a[i], a[min_index] = a[min_index], a[i] # 交换,
        # 将从未排序区间中找到的最小的元素,将其放到已排序区间的末尾。
    return a

res = selectionSort([5,1,4,6,3,2])
print(res) # [1, 2, 3, 4, 5, 6]

三个问题

  • 1.选择排序是原地排序算法吗?
  • 2.选择排序是稳定的排序算法吗?

不是. 选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。

比如 5,8,5,2,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素 2,与第一个 5 交换位置,那第一个 5 和中间的 5 顺序就变了,所以就不稳定了。正是因此,相对于冒泡排序和插入排序,选择排序就稍微逊色了。

  • 3.选择排序的时间复杂度是多少?

最好情况时间复杂度O(n^2)
最坏情况时间复杂度O(n^2)
平均情况时间复杂度 O(n^2)

总结

原地排序稳定最好 最坏 平均
冒泡排序O(n) O(n^2) O(n^2)
插入排序O(n) O(n^2) O(n^2)
选择排序×O(n^2) O(n^2) O(n^2)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值