目录

排序算法执行效率的分析,从这几个方面来衡量

冒泡排序(Bubble Sort)

性能分析:

插入排序(Insertion Sort)

性能分析:

选择排序(Selection Sort)

性能分析:

冒泡排序、插入排序、选择排序三者比较

归并排序

性能分析:

快速排序

性能分析:

快排和归并的对比

桶排序(Bucket sort)

计数排序(Counting sort)

约束条件:

基数排序(Radix sort)

约束条件


排序算法执行效率的分析,从这几个方面来衡量

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

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

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

原地排序(Sorted in place)。原地排序算法,就是特指空间复杂度是 O(1) 的排序算法

排序算法的稳定性:如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变,如果两个 3 的前后顺序没有改变,那我们就把这种排序算法叫作稳定的排序算法

冒泡排序(Bubble Sort)

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。


// 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {if (n <= 1) return;for (int i = 0; i < n; ++i) {// 提前退出冒泡循环的标志位boolean flag = false;for (int j = 0; j < n - i - 1; ++j) {if (a[j] > a[j+1]) { // 交换int tmp = a[j];a[j] = a[j+1];a[j+1] = tmp;flag = true;  // 表示有数据交换      }}if (!flag) break;  // 没有数据交换,提前退出}
}

性能分析:

1、冒泡排序空间复杂度为 O(1),是一个原地排序算法

2、冒泡排序中,只有交换才可以改变两个元素的前后顺序,所以冒泡排序是稳定的排序算法

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

而最坏的情况是,要排序的数据刚好是倒序排列的,需要进行 n 次冒泡操作,所以最坏情况时间复杂度为 O(n^2)。

插入排序(Insertion Sort)

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

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

当需要将一个数据 a 插入到已排序区间时,需要拿 a 与已排序区间的元素依次比较大小,找到合适的插入位置。找到插入点之后,还需要将插入点之后的元素顺序往后移动一位,这样才能腾出位置给元素 a 插入。

代码如下


// 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {if (n <= 1) return;for (int i = 1; i < n; ++i) {int value = a[i];int j = i - 1;// 查找插入的位置for (; j >= 0; --j) {if (a[j] > value) {a[j+1] = a[j];  // 数据移动} else {break;}}a[j+1] = value; // 插入数据}
}

性能分析:

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

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

3、如果从尾到头在有序数据组里面查找插入位置,这种情况下,最好是时间复杂度为 O(n)

如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以最坏情况时间复杂度为 O(n^2)。

选择排序(Selection Sort)

选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

性能分析:

1、选择排序空间复杂度为 O(1),是一种原地排序算法

2、选择排序的最好情况时间复杂度、最坏情况和平均情况时间复杂度都为 O(n^2)

3、选择排序是一种不稳定的排序算法。从我前面画的那张图中,你可以看出来,选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。

冒泡排序、插入排序、选择排序三者比较

归并排序

归并排序的核心思想:如果要排序一个数组,先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。

归并排序使用的就是分治思想。分治,顾名思义,就是分而治之,将一个大问题分解成小的子问题来解决。小的子问题解决了,大问题也就解决了。

如图所示,申请一个临时数组 tmp,大小与 A[p...r]相同。用两个游标 i 和 j,分别指向 A[p...q]和 A[q+1...r]的第一个元素。比较这两个元素 A[i]和 A[j],如果 A[i]<=A[j],就把 A[i]放入到临时数组 tmp,并且 i 后移一位,否则将 A[j]放入到数组 tmp,j 后移一位。继续上述比较过程,直到其中一个子数组中的所有数据都放入临时数组中,再把另一个数组中的数据依次加入到临时数组的末尾,这个时候,临时数组中存储的就是两个子数组合并之后的结果了。最后再把临时数组 tmp 中的数据拷贝到原数组 A[p...r]中。

性能分析:

1、归并排序是一个稳定的排序算法,在合并前后的先后顺序不变

2、归并排序的执行效率与要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 O(nlogn)。

3、归并排序不是原地排序算法。空间复杂度为O(n)

尽管每次合并操作都需要申请额外的内存空间,但在合并完成之后,临时开辟的内存空间就被释放掉了。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)。

快速排序

快排的思想:如果要排序数组中下标从 p 到 r 之间的一组数据,选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。

性能分析:

1、原地排序算法

2、不稳定的排序算法

3、T(n) 在大部分情况下的时间复杂度都可以做到 O(nlogn),只有在极端情况下,才会退化到 O(n2)

快排和归并的对比

1、处理方式:归并排序的处理过程是由下到上的,先处理子问题,然后再合并。而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题

2、归并排序虽然是稳定的、时间复杂度为 O(nlogn) 的排序算法,但是它是非原地排序算法,主要原因是合并函数无法在原地执行

快速排序通过设计巧妙的原地分区函数,可以实现原地排序,解决了归并排序占用太多内存的问题。

3、归并排序算法是一种在任何情况下时间复杂度都比较稳定的排序算法,这也使它存在致命的缺点,即归并排序不是原地排序算法,空间复杂度比较高,是 O(n)。正因为此,它也没有快排应用广泛。

快速排序算法虽然最坏情况下的时间复杂度是 O(n2),但是平均情况下时间复杂度都是 O(nlogn)。不仅如此,快速排序算法时间复杂度退化到 O(n2) 的概率非常小,可以通过合理地选择 pivot 来避免这种情况。

桶排序(Bucket sort)

核心思想:将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。

约束条件:

1、要排序的数据需要很容易就能划分成 m 个桶,并且,桶与桶之间有着天然的大小顺序。这样每个桶内的数据都排序完之后,桶与桶之间的数据不需要再进行排序

2、数据在各个桶之间的分布是比较均匀的,如果数据经过桶的划分之后,有些桶里的数据非常多,有些非常少,很不平均,那桶内数据排序的时间复杂度就不是常量级了

3、桶排序比较适合用在外部排序中,所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。

时间复杂度:O(n)

计数排序(Counting sort)

计数排序其实是桶排序的一种特殊情况。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k,就可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。

比如给50万考生排名,考生的满分是 900 分,最小是 0 分,这个数据的范围很小,所以我们可以分成 901 个桶,实现计数排序,桶排序的粒度就不一定那么细了,可以分为0-10分为一桶,11-20为一桶


// 计数排序,a是数组,n是数组大小。假设数组中存储的都是非负整数。
public void countingSort(int[] a, int n) {if (n <= 1) return;// 查找数组中数据的范围int max = a[0];for (int i = 1; i < n; ++i) {if (max < a[i]) {max = a[i];}}int[] c = new int[max + 1]; // 申请一个计数数组c,下标大小[0,max]for (int i = 0; i <= max; ++i) {c[i] = 0;}// 计算每个元素的个数,放入c中for (int i = 0; i < n; ++i) {c[a[i]]++;}// 依次累加for (int i = 1; i <= max; ++i) {c[i] = c[i-1] + c[i];}// 临时数组r,存储排序之后的结果int[] r = new int[n];// 计算排序的关键步骤,有点难理解for (int i = n - 1; i >= 0; --i) {int index = c[a[i]]-1;r[index] = a[i];c[a[i]]--;}// 将结果拷贝给a数组for (int i = 0; i < n; ++i) {a[i] = r[i];}
}

约束条件:

计数排序只能用在数据范围不大的场景中,如果数据范围 k 比要排序的数据 n 大很多,就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,要将其在不改变相对大小的情况下,转化为非负整数。

基数排序(Radix sort)

给上面数据排序,先按照最后一位来排序字符,然后,再按照倒数第二位重新排序,以此类推,最后按照第一位重新排序。经过 3次排序之后,字符就都有序了

约束条件

基数排序对要排序的数据是有要求的,需要可以分割出独立的“位”来比较,而且位之间有递进的关系,如果 a 数据的高位比 b 数据大,那剩下的低位就不用比较了。除此之外,每一位的数据范围不能太大,要可以用线性排序算法来排序,否则,基数排序的时间复杂度就无法做到 O(n) 了。

数据结构排序学习总结相关推荐

  1. 【数据结构排序算法系列】数据结构八大排序算法

    排序算法在计算机应用中随处可见,如Windows操作系统的文件管理中会自动对用户创建的文件按照一定的规则排序(这个规则用户可以自定义,默认按照文件名排序)因此熟练掌握各种排序算法是非常重要的,本博客将 ...

  2. 【实战笔记】Java 算法与数据结构-排序(选择、插入、冒泡、希尔、归并、快速、堆)

    文章目录 基础排序算法O(n^2) 选择排序 插入排序及优化 冒泡排序及优化 希尔排序及优化 高级排序算法O(nlogn) 归并排序及优化 快速排序及优化 堆和堆排序 排序算法总结 本文为慕课网实战课 ...

  3. 【推荐系统】推荐系统中的排序学习

    " 本文首先介绍排序学习的三种主要类别,然后详细介绍推荐领域最常用的两种高层排序学习算法框架:BPR和LambdaMART.因为排序学习的算法和实践大都来源于信息检索,一些理论也必须从信息检 ...

  4. ACL 2020 | 基于多级排序学习的层次化实体标注

    ©PaperWeekly 原创 · 作者|龚俊民 学校|新南威尔士大学硕士生 研究方向|NLP.可解释学习 论文标题:Hierarchical Entity Typing via Multi-leve ...

  5. ES6基础4(数据结构)-学习笔记

    文章目录 ES6基础4(数据结构)-学习笔记 set map symbol ES6基础4(数据结构)-学习笔记 set //set 数据结构 类似数组 成员信息唯一性var s = new Set() ...

  6. 数据结构---排序算法的总结

    数据结构-排序算法的总结 分类 冒泡排序,时间复杂度O(n x n),空间复杂度O(1),稳定 简单选择排序,时间复杂度O(n x n),空间复杂度O(1),不稳定 希尔排序,时间复杂度O(n^1.3 ...

  7. 深入浅出排序学习:写给程序员的算法系统开发实践

    引言 我们正处在一个知识爆炸的时代,伴随着信息量的剧增和人工智能的蓬勃发展,互联网公司越发具有强烈的个性化.智能化信息展示的需求.而信息展示个性化的典型应用主要包括搜索列表.推荐列表.广告展示等等. ...

  8. 数据结构-排序基础代码

    数据结构-排序基础代码 1.快排的递归算法: void QuickSort(int A[],int n){Qsort(A,0,n-1); } void Qsort(int A[],int left,i ...

  9. ICCV 2019 | RankSRGAN:排序学习 + GAN 用于超分辨率

    本文作者:肖健  在读博士 作者学校:哈尔滨工程大学 研究方向:生成对抗网络,视频处理,目标检测. 前言 本文将对ICCV2019的Oral论文<RankSRGAN:Generative Adv ...

最新文章

  1. 【NOIP2015模拟10.22】最小代价
  2. 微信开发同步微信服务器图片到本地,逻辑处理。
  3. .net Mvc Controller 接收 Json/post方式 数组 字典 类型 复杂对象
  4. 【配置文件】大家来谈谈这个log4j到底有什么用?
  5. idea中git如何切换到master_IDEA中Git的使用
  6. PCB布局中丝印大小及位置调节方法
  7. it男java_java-学习8
  8. tensorflow中命名空间、变量命名的问题
  9. 【C#】wpf查找父子节点
  10. 让XP远程桌面连接支持网络身份验证
  11. span标签的间距问题
  12. 豫教科计算机资源管理教案,豫科版小学五年级上册信息技术教案(全册.doc
  13. element-ui+vue,翻页添加首页、尾页跳转按钮
  14. 爬虫chromedriver被识别怎么办?
  15. Win10锁屏自动暂停音乐播放,解锁后恢复
  16. 我的前半生之十四,谈论富婆是一种高级趣味
  17. 云存储哪家强:AWS、Azure、Google、SoftLayer
  18. 区块链如何破解供应链金融痛点
  19. 微信小程序_文档_08_组件_媒体组件_地图_画布_开放能力
  20. VR全景拍摄中心点是如何对齐的?

热门文章

  1. linux体验服务器,体验Ubuntu做服务器
  2. 置换怎么表示成轮换_开门红 新纪录!首场电车置换引爆州城,两小时突破160辆!...
  3. 配置phoenix连接hbase_Phoenix4.14不读取hbase配置文件-问答-阿里云开发者社区-阿里云...
  4. 原生编辑器_免费开源的GIF制作神器,可录屏幕/摄像头/画板,自带编辑器
  5. 微型计算机的典型应用场景,单片机有哪些类型和应用场景?-MCU解决方案
  6. java下载本地目录excel_java写简单Excel 首行是目录 然后前台下载
  7. 内核功能导致重启_诊断修复 TiDB Operator 在 K8s 测试中遇到的 Linux 内核问题
  8. 在行列都排好序的矩阵中找数
  9. 推荐系统笔记(模型融合)
  10. 堆内存与栈内存的区别