来源 | 小齐本齐

封图 | CSDN 付费下载自视觉中国

插入排序

借用《算法导论》里的例子,就是我们打牌的时候,每新拿一张牌都会把它按顺序插入,这,其实就是插入排序。

齐姐声明:虽然我们用打牌的例子,但是可不能学胡适先生啊。

对于数组来说怎么做呢?

有一个重要的思想,叫做挡板法,就是用挡板把数组分成两个区间:

  • 挡板左边:已排序

  • 挡板右边:未排序

那么排序分三步走

  1. 最初挡板是在数组的最左边,保证已排序区间里一个数都没有,或者也可以包含一个数啦;

  2. 核心思想就是:
    依次遍历未排序区间里的元素,在已排序区间里找到正确的位置插入;

  3. 重复这个过程,直到未排序区间为空。

举个例子:{5, 2, 1, 0}

第一步,挡板最初在这里:

第二步, 把 2 插入已排序区间的正确位置,变成:

重复这个步骤,把 1 排好:

最后把 0 排好:

那代码也很简单:

public void insertionSort(int[] input) {if(input == null || input.length <= 1) {return;}for(int i = 1; i < input.length; i++) {int tmp = input[i];int j = i - 1;while(j >= 0 && input[j] > tmp) {input[j+1] = input[j];j --;}input[j+1] = tmp;}
}

我们来分析一下这个算法的时空复杂度。

时间复杂度

关于时间复杂度大 O 有两个要点

  • 是描述随着自变量的增长,所需时间的增长率;

  • 是渐近线复杂度,就是说

    • 不看系数

    • 只看最高阶项

那么我们关心的 worst case 的情况就是:
如果数组是近乎倒序的,每次插入都要在数组的第一个位置插入,那么已排序区间内的所有的元素都要往后移动一位,这一步平均是 O(n),那么重复 n 次就是 O(n^2).

空间复杂度

重点是一个峰值的概念,并不是累计使用的空间。

这里是 O(1) 没什么好说的。

引入一个概念:sorted in place,也就是原地排序

原地排序就是指空间复杂度为 O(1) 的算法,因为没有占用额外的空间,就是原地打转嘛。

其实 in-place 的思想并不是只在排序算法里有,只不过排序算法是一个最广为人知的例子罢了。本质上就是一个节省使用空间的思想。

但是对于排序算法,只分析它的时空复杂度是不够的,还有另外一个重要指标:

稳定性

意思是元素之间的相对顺序是否保持了不变。

比如说:{5, 2, 2, 1, 0}

这个数组排序完成后这里面的两个 2 的相对顺序没有变,那么这个排序就是一个稳定排序。

那有同学可能就想,顺序变了又有什么关系呢?

其实,在实际工作中我们排序的对象不会只是一个数字,而是一个个的对象 (object),那么先按照对象的一个性质来排序,再按照另一个性质来排序,那就不希望原来的那个顺序被改变了。好像有点抽象,我们举个例子。

比如在股票交易系统里,有买卖双方的报价,那是如何匹配的呢?

  • 先按照价格排序;

  • 在相等的价格中,按照出价的时间顺序来排序。

那么一般来说系统会维持一个按时间排序的价格序列,那么此时只需要用一个具有稳定性的排序算法,再按照价格大小来排序就好了。因为稳定性的排序算法可以保持大小相同的两个对象仍维持着原来的时间顺序。

那么插入排序是否是稳定性的排序呢?

答案是肯定的。因为在我们插入新元素的时候是从后往前检查,并不是像打牌的时候随便插一个位置不能保证相对顺序。

大家可以看下面的动画[1] 就非常清楚了~

优化

插入排序其实是有很大的优化空间的,你可以搜一下“希尔排序”。

在刚开始学习的时候,深度固然重要,但因为广度不够,如果学的太深可能会很痛苦,一个知识点就无穷无尽的延展,这并不是一个高效的学习方式。

所以如果时间有限,就要做好深度和广度的平衡:

  • 在常用常考的知识点上多花时间精力,追求深度;

  • 在一些拓展性的知识点上点到为止,先知道有这么回事就行。

保持 open minded 的心态,后期就会有质的提高。

选择排序

选择排序也是利用了“挡板法”这个经典思想。

挡板左边是已排序区间,右边是未排序区间,那么每次的“选择”是去找右边未排序区间的最小值,找到之后和挡板后面的第一个值换一下,然后再把挡板往右移动一位,保证排好序的这些元素在挡板的左边。

比如之前的例子:{5, 2, 0, 1}

我们用一个挡板来分隔数组是否排好序,
用指针 j 来寻找未排序区间的最小值;

第一轮 j 最初指向 5,然后遍历整个未排序区间,最终指向 0,那么 0 就和挡板后的第一个元素换一下,也就是和 5 交换一下位置,挡板向右移动一位,结束第一轮。

第二轮,j 从挡板后的2开始遍历,最终指向1,然后1和挡板后的第一个元素 2 换一下,挡板向右移动一位,结束第二轮。

第三轮,j 从2开始遍历,最终指向2,然后和2自己换一下,挡板向右移动一位,结束第三轮。

还剩一个元素,不用遍历了,就结束了。

选择排序与之前的插入排序对比来看,要注意两点:

  1. 挡板必须从 0 开始,而不能从 1 开始。虽然在这两种算法中,挡板的物理意义都是分隔已排序和未排序区间,但是它们的已排序区间里放的元素的意义不同:

  • 选择排序是只能把当前的最小值放进来,而不能放其他的;

  • 插入排序的第一个元素可以为任意值。

所以选择排序的挡板左边最开始不能有任何元素。

  1. 在外层循环时,

  • 选择排序的最后一轮可以省略,因为只剩下最大的那个元素了;

  • 插入排序的最后一轮不可省略,因为它的位置还没定呢。

class Solution {public void selectionSort(int[] input) {if(input == null || input.length <= 1) {return;} for(int i = 0; i < input.length - 1; i++) {int minValueIndex = i;for(int j = i + 1; j < input.length; j++) {if(input[j] < input[minValueIndex]) {minValueIndex = j;}}swap(input, minValueIndex, i);}}private void swap(int[] input, int x, int y) {int tmp = input[x];input[x] = input[y];input[y] = tmp;}
}

时间复杂度

最内层的 if 语句每执行一次是 O(1) ,那么要执行多少次呢?

  • 当 i = 0 时,是 n-1 次;

  • 当 i = 1 时,是 n-2 次;

  • ...

  • 最后是 1 次;

所以加起来,总共是:
(n-1) + (n-2) + … + 1 = n*(n-1) / 2 = O(n^2)

是这样算出来的,而不是一拍脑袋说两层循环就是 O(n^2).

空间复杂度

这个很简单,最多的情况是 call swap() 的时候,然后 call stack 上每一层就用了几个有限的变量,所以是 O(1)。

那自然也是原地排序算法了。

稳定性

这个答案是否定的,选择排序并没有稳定性。

因为交换的过程破坏了原有的相对顺序,比如: {5, 5, 2, 1, 0} 这个例子,第一次交换是 0 和 第一个 5 交换,于是第一个 5 跑到了数组的最后一位,且再也无翻身之地,所以第一个 5 第二个 5 的相对顺序就已经打乱了。

这个问题在石头哥的那篇谷歌面经文章里有被考到哦,如果还没有看过这篇面经文章的,在公众号里回复「谷歌」二字,就可以看到了。

优化

选择排序的其中一步是选出每一轮的最小值,那么这一步如果使用 heapify() 来优化,就可以从 O(n) 优化到 O(logn),这其实就变成了 heapSort.

推荐阅读
  • 追忆童年,教你用Python画出儿时卡通人物

  • 如何用NLP辅助投资分析?三大海外机构落地案例详解

  • Gary Marcus:因果熵理论的荒诞和认知科学带给AI的11个启示 | 文末赠书

  • AI 终极问题:我们的大脑是一台超级计算机吗?

  • 借助大数据进行社交媒体营销,企业们得这么玩!

  • 力挺比特币的世界第2交易员:仅次于索罗斯,连续25年无亏损

你点的每个“在看”,我都认真当成了AI

漫画 | 程序媛小姐姐带你一次了解什么是排序算法相关推荐

  1. 神仙程序媛小姐姐的23个Java设计模式 ,全站式保姆的Java教程导航帖(已完结)

    Java的23个Java设计模式 ,已完结,以后的时间不定期补番.还有一些了解即可,不需要使用,现实开发用不掉 之前,分多篇讲解Java23种设计模式,毕竟太散,现将全部内容进行汇总.大家以后学习23 ...

  2. 程序媛小姐姐甜甜恋爱过程

    程序媛小姐姐甜甜恋爱过程 呵,当了程序媛还想谈恋爱,谈个屁吧你.单身一辈子吧!!! 呵,当了程序媛还想谈恋爱,谈个屁吧你.单身一辈子吧!!!

  3. 神仙程序媛小姐姐的一些列Java教程,从小白到进阶,春招和秋招必备的面试题,全站式保姆的Java教程导航帖(未完结)

    Java入门教程导航,未完结,以后的时间不定期补番. Java基础篇 (入门阶段) 小姐姐教你:java环境变量 的配置与详解(全网最详细教程 小姐姐手把手教你最基础Java语法,快来我的碗中 [Ja ...

  4. 代码写对了还挂了?程序媛小姐姐从 LRU Cache 带你看面试的本质

    来源 | 码农田小齐 责编 |  Carol 前言 在讲这道题之前,我想先聊聊「技术面试究竟是在考什么」这个问题. 技术面试究竟在考什么 在人人都知道刷题的今天,面试官也都知道大家会刷题准备面试,代码 ...

  5. 程序员小姐姐用代码实现《本草纲目》毽子操,刘畊宏亲自回复:很cool!

    点击"开发者技术前线",选择"星标" 让一部分开发者看到未来 来自 | 程序人生 (ID:coder_life) "腰间的赘肉咔咔掉!人鱼线马甲线我想 ...

  6. 程序员小姐姐写出代码版《本草纲目》毽子操,附上源码 !

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 来源:程序人生 (ID:coder_life) "腰间的赘肉咔咔掉!人鱼线马甲线我想要!",&qu ...

  7. 曾在字节实习的程序员小姐姐,教你一步提取动漫线稿!比用PS更清晰

    丰色 发自 凹非寺 量子位 报道 | 公众号 QbitAI 每当你看到喜欢的动漫作品,想把线稿提出来.进行重新上色等加工时,你是怎么做的? 用PS吗? 是不是有点麻烦?结果还很糊? GitHub上有一 ...

  8. 女神节 | 程序员小姐姐把这份巧克力,送给你

    说起程序员, 很多人会第一时间想到 "短发.格子衬衣.运动鞋"的男生形象. 在大多数人的观念中, 编程这种需要理性思维的工作 还是更适合男生来做. 但这都是人们的偏见而已, 在男女 ...

  9. 【AI学院】有三AI博士小姐姐带你学深度学习之图像识别下篇,附上篇回顾

    带学营第一期(上)圆满结束回顾 有三 AI 深度学习之图像识别带学营第一期(上)圆满结束,这一期我们主要的培训内容是深度学习基础+项目实战(人脸表情识别和鸟类细粒度识别). 以上的课程可以说是干货满满 ...

最新文章

  1. 10、HTML的基本结构
  2. Python爬虫快速入门,BeautifulSoup基本使用及实践
  3. Android Studio 2.1.2 升级到 2.2之后,gradle 编译版本更新为2.2.0,databinding报错
  4. linux防火墙常用缩写,Linux iptables常用防火墙规则
  5. 英国文化影响管理风格_文化如何影响用户体验
  6. async中series的实现 javascript构件
  7. C++学习之路 | PTA乙级—— 1023 组个最小数 (20分)(精简)
  8. ASP.NET Core 2.1 : 十二.内置日志、使用Nlog将日志输出到文件
  9. LeetCode之第一个错误版本
  10. php 生僻字 拼音,php 汉字转拼音 [包含20902个基本汉字+5059生僻字]
  11. 电气技术应用和计算机应用,电气技术应用专业介绍 ppt课件.ppt
  12. 【白话经典算法系列之十二】数组中只出现1次的两个数字(百度面试题)
  13. Office 2010安装程序包的语言不受系统支持
  14. Android 适配时用到的限定词
  15. python3D绘图Axes3D函数详解
  16. 手机刷机的几种常用方法
  17. 删除OneDrive for Bussiness导航栏快捷方式
  18. JavaWeb核心技术系列教程(23)——JSP标签
  19. 计算机网络期末冲刺复习
  20. 设备中LPC2368芯片个例参数问题导致故障的分析

热门文章

  1. linux下磁盘镜像软件DRBD的使用
  2. spring+ (activeMQ) 实现queue与topic
  3. 分享Kali Linux 2017年第11周镜像文件
  4. golang笔记——struct
  5. 设计模式----组合模式UML和实现代码
  6. deepin开通ssh
  7. 近段时间学习html和CSS的一些细碎总结
  8. Java 基础【04】Swing 组件事件注册
  9. 山寨上网本溃败的两点教训
  10. git 使用和一些错误