倒序排序_排序(上)

297856445e7afaa48ed787f16d09a523.png

几种经典排序算法及其时间复杂度级别

  • 冒泡、插入、选择 O(n^2) 基于比较

  • 快排、归并 O(nlogn) 基于比较

  • 计数、基数、桶 O(n) 不基于比较

如何分析一个排序算法

  • 学习排序算法的思路

    • 明确原理、掌握实现以及分析性能

  • 如何分析排序算法性能

    • 从执行效率、内存消耗以及稳定性 3 个方面分析排序算法的性能

  • 执行效率:从以下 3 个方面来衡量

    • 最好情况、最坏情况、平均情况时间复杂度

    • 时间复杂度的系数、常数、低阶:排序的数据量比较小时考虑

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

  • 内存消耗

    • 通过空间复杂度来衡量。针对排序算法的空间复杂度,引入原地排序的概念,原地排序算法就是指空间复杂度为 O(1) 的排序算法

  • 稳定性

    • 如果待排序的序列中存在值等的元素,经过排序之后,相等元素之间原有的先后顺序不变,就说明这个排序算法时稳定的

冒泡排序

  • 排序原理

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

    • 对相邻两个数据进行比较,看是否满足大小关系要求,若不满足让它俩互换

    • 一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作

    • 优化:若某次冒泡不存在数据交换,则说明已经达到完全有序,所以终止冒泡

  • 代码实现

  • 性能分析

  • 执行效率:最好时间复杂度、最坏时间复杂度、平均时间复杂度

    • 最好情况时间复杂度:数据完全有序时,只需进行一次冒泡操作即可,时间复杂度是O(n)

    • 最坏情况时间复杂度:数据倒序排序时,需要n次冒泡操作,时间复杂度是O(n^2)

    • 平均情况时间复杂度:通过有序度和逆序度来分析

    • 什么是有序度

      • 有序度是数组中具有有序关系的元素对的个数,比如 [2,4,3,1,5,6] 这组数据的有序度就是 11,分别是[2,4] [2,3] [2,5] [2,6] [4,5] [4,6] [3,5] [3,6] [1,5] [1,6] [5,6]。同理,对于一个倒序数组,比如[6,5,4,3,2,1],有序度是 0;对于一个完全有序的数组,比如 [1,2,3,4,5,6],有序度为 n*(n-1)/2,也就是 15,完全有序的情况称为满有序度

    • 什么是逆序度

      • 逆序度的定义正好和有序度相反。核心公式:逆序度 = 满有序度 - 有序度

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

    • 冒泡排序包含两个操作原子,即比较和交换,每交换一次,有序度加 1。不管算法如何改进,交换的次数总是确定的,即逆序度

    • 对于包含 n 个数据的数组进行冒泡排序,平均交换次数是多少呢?最坏的情况初始有序度为 0,所以要进行 n * (n-1) / 2交换。最好情况下,初始状态有序度是n * (n-1) / 2,就不需要进行交互。我们可以取个中间值n*(n-1) / 4,来表示初始有序度既不是很高也不是很低的平均情况

    • 换句话说,平均情况下,需要 n * (n-1) / 4 次交换操作,比较操作可定比交换操作多,而复杂度的上限是 O(n^2),所以平均情况时间复杂度就是 O(n^2)

    • 以上的分析并不严格,但很实用,这就够了

  • 空间复杂度:每次交换仅需 1 个临时变量,故空间复杂度为 O(1),是原地排序算法

  • 算法稳定性:如果两个值相等,就不会交换位置,故是稳定排序算法

插入排序

  • 算法原理

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

  • 代码实现

  • 性能分析

    • 时间复杂度:最好、最坏、平均情况

      • 如果要排序的数组已经是有序的,我们并不需要搬移任何数据。只需要遍历一遍数组即可,所以时间复杂度是 O(n)。如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,因此时间复杂度是 O(n^2)。而在一个数组中插入一个元素的平均时间复杂都是 O(n),插入排序需要n次插入,所以平均时间复杂度是 O(n^2)

    • 空间复杂度:从上面的代码可以看出,插入排序算法的运行并不需要额外的存储空间,所以空间复杂度是 O(1),是原地排序算法

    • 算法稳定性:在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现的元素的后面,这样就保持原有的顺序不变,所以是稳定的

为什么插入排序比冒泡排序更受欢迎

  • 代码实现上来看,冒泡排序的数据交换要⽐插⼊排序的数据移动要复杂,冒泡排序需要 3 个赋值操作,⽽插⼊排序只需要 1 个

  • 执⾏⼀个赋值语句的时间粗略地计为单位时间(unit_time),然后分别⽤冒泡排序和插⼊排序对同⼀个逆序度是 K 的数组进⾏排序。⽤冒泡排序,需要 K 次交换操作,每次需要 3 个赋值语句,所以交换操作总耗时就是3 * K 单位时间。⽽插⼊排序中数据移动操作只需要 K 个单位时间

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值