1177: 按要求排序(指针专题)_排序算法

关于排序算法,可以参考这篇文章。

十大经典排序算法动画与解析,看我就够了!(配代码完全版)​mp.weixin.qq.com
50411679ece416220926f109971c8e3c.png

什么是排序?

排序本质上是在消除逆序对[1]

将数字按特定大小的顺序排列,大体有两大类排序方式。

  • 比较类排序:通过两两数字之间的数值比较
  • 非比较类排序:借用数字本身的数值特征排序

比较类排序:

  • 冒泡排序:遍历数列,两两比较,若是前者大于后者则交换位置。第一次遍历的结果是把最大的放到末尾,第二次是把第二大的放在倒二...直到排序完成[2]
  • 选择排序:将第一个位置的数值与剩余数值比较,若是剩余数值中最小的有比它小,则交换位置,以此类推第二个位置...
  • 插入排序:从第二个位置开始,逐一和前面的位置比较,在比较的过程中与比自己大的交换位置,直到自己比它大时停止。以此类推从第三个位置...
这些排序都是从一侧方向开始,不断累积有序数列。
  • 希尔排序:
  1. 根据间隔将一个数列划分成好几个序列(若数列长度为10,间隔为5,此时平分数列)
  2. 按照顺序对比每一个数列中的数值(0与5,1与6,2与7...),意味着在间隔内首尾的两个数是有序的
  3. 之后不断缩小间隔,直至降到1(1就是两两比较)。

关于希尔排序推荐冒泡,Java3y的回答:

  • 归并排序:
  1. 通过递归的方式将一个完整的数列不断划分,直至最小为长度1或2的序列。
  2. 利用两个指针扫描两个序列做比较,拼凑成一个有序序列。
  3. 借助递归的归可以不断重复这个拼装过程

归并排序是树的后序遍历,因为“动手的时机在划分之后”

  • 快速排序:
  1. 选择一个数当作基准
  2. 两个指针分别从头尾开始移动到中间,
  3. 左指针遇到比基准大的数停下,右指针遇到比基准小的数停下,左右指针互换数值
  4. 两指针相遇后停下,与基准交换位置。
  5. 继续递归调用基准左边的序列和右边的序列,重复1,2步

第二步中两个指针的作用是将一个序列按照基准划分成两边,一边小一边大。你可以将两个指针“都从头开始”,不用“头尾”分开,也能实现这个目的,相当于快慢指针(跳转)。慢指针停留在头部等待交换,快指针不断往前寻找比基准小的数与慢指针交换数值,快指针跑完,就把基准与慢指针交换。
快速排序是树的前序遍历,因为“动手的时机在划分之前”

  • 堆排序:
  1. 创建一个堆[3](从数列中间开始,由后往前的对​
    equation?tex=i 与​
    equation?tex=2%2Ai%2B1
    equation?tex=2%2Ai%2B2 ​三个数值堆化)
  2. 按从后往前的顺序将数列中的每个数字与第一个位置交换,每次交换都重新将顶点
    equation?tex=%EF%BC%880%EF%BC%8C1%EF%BC%8C2%EF%BC%89 堆化

第一步从后往前的堆化,相当于将父节点与子节点排序,意味着序列内​

equation?tex=i 与​
equation?tex=2%2Ai%2B1
equation?tex=2%2Ai%2B2 ​​组成了两个有序数列(​​
equation?tex=2%2Ai%2B1
equation?tex=2%2Ai%2B2 ​为一个整体与
equation?tex=i ​比较),且
第一个数值为最值。
第二步的交换(最后一个与第一个)是 将最值放入结果当中。例如此时是最大堆(最大值在顶上),则把最大值与最后一个位置更换,且把 位置抹去不参与后续的排序,之后再比较
equation?tex=%EF%BC%880%EF%BC%8C1%EF%BC%8C2%EF%BC%89 ​选出最大值。

第二步的过程就相当于你只要把最顶上的数值拿走,下面的数值就会根据规则 不断上浮。最值会被放到最后,且 不参与上浮过程,所以排序后会对调排序位置,比如原先是最小堆但是排出来后是从大到小的顺序。

非比较类排序:

  • 计数排序:将数值当作序列下标,序列中相应下标的值记做数值出现的次数。数值次数记录完成后,遍历序列,按照序列中的记录次数输出下标。
    缺点很明显,占空间,且时间复杂度是按照最大数值来计算而非数值个数,若范围太大,空间和时间都会增加。
  • 桶排序:在计数排序的基础上加以改进。既然空间过大,那就只给他特定的大小(X个桶),通过规则(例如除余)将数字放入桶中。接着用比较类排序对有数字的桶进行排序,之后再遍历桶一个个取出来。
  • 基数排序:从位数下手,先按照个位数大小放到桶里再拿出(排序一次),再按照十位数大小放到桶里再拿出(排序两次)...最大数有几位就排序几次。

前三种的比较排序,平均时间复杂度在

equation?tex=O%28n%5E2%29 ​,而后四种则在
equation?tex=O%28nlogn%29 ​,最主要的原因在于每次交换时,
消除逆序对的对数不同。同样是 “从一侧方向累加有序序列”,堆结构就能优化这样的方案。

希尔,归并,快速,都在用各种“组装技巧”。归并老老实实的分好,从“两两比较”开始拼装;希尔用“不断缩小的间隔(增量)”来分组合并;快排是“随机取样”的不断对半分(并不是对半)。

非比较类排序则是额外开辟空间,按照数字本身的规则进行划分,比较依赖序列本身的分布状况

顾不问:算法入门笔记-目录​zhuanlan.zhihu.com
zhihu-card-default.svg
github​github.com

参考

  1. ^假设序列为5,4,3,2,1要从小到大排列,逆序对就是54,53,52...一共有十对
  2. ^以下顺序默认从小到大,遍历也可以相反方向,这里只为方便描述
  3. ^堆的性质是父节点始终大于等于or小于等于子节点,映射到序列就是:若i位置为父节点,则i同时大于2*i+1和2*i+2的子节点。堆化是将无序序列变成堆的过程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值