冒泡排序,插入排序,选择排序三种算法的优劣

最近听了王争老师的数据结构与算法之美,大有获益,特写此博客与大家分享.

排序算法太多了,但大体可以归结于三类,冒泡排序,插入排序,选择排序,那么如果分析一个算法呢,评价一个算法的优劣呢,可以从三方面入手,1.排序算法的执行效率,2,排序算法的内存消耗 3,排序算法的稳定性,下面咱们先从这三方面来学会如何分析算法.

一:排序算法的执行效率

针对执行效率可以从以下几点来分析:

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

为什么要区分这三种时间复杂度呢?第一,有些排序算法会区分,为了好对比,所以我们最好都做一下区分.第二,对于要排序的数据,有的接近有序,有的完全无序。有序度不同的数据,对于排序的执行时间肯定是有影响的,我们要知道排序算法在不同数据下的性能表现。

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

我们知道,时间复杂度反应的是数据规模 n 很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶.但是实际的软件开发中,我们排序的可能是 10 个、100 个.1000 个这样规模很小的数据,所以,在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来.

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

基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动.所以我们在分析排序算法的执行效率的时候,比较次数和交换(或移动次数)也要考虑进

二:排序算法的内存消耗

算法的内存消耗可以通过空间复杂度来衡量,排序算法也是一样.不过针对排序算法的空间复杂度,我们还引入一个新的概念,原地排序.原地排序算法,就是特指空间复杂度是 O(1) 的排序算法,今天我们讲的三种算法,都是原地排序算法.

三:排序算法的稳定性

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

这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后就是 2,3,3,4,8,9。

这组数据里有两个 3。经过某种排序算法排序之后,如果两个 3的前后顺序没有改变,我们就把这种排序算法叫稳定的排序算法.如果前后顺序发生改变,这种算法就是不稳定排序算法.

但是这种稳定性对排序有什么影响呢?举个例子,比如说我们现在要给电商交易系统中的订单进行排序,要按照下单时间,和交易金额两个维度进行排序.我们希望按照金额从小到大排序,时间从早到晚排序,对于相同金额,按照从早到晚排序.

最先想到的办法是:先按照订单金额进行排序,然后遍历排序好的数据,对于金额相同的小区间,按照时间从早到晚再排序.这种排序思路不难,但实行起来却不简单.

借助稳定排序算法,这个问题可以非常简洁地解决.我们可以先按照订单时间 进行排序,排序完成后我们按照稳定排序算法再按照订单金额进行排序,两遍排序之后,我们的订单金额是按照从小到大进行排序,金额相同的是按照时间从早到晚进行排序.为什么呢?稳定排序算法可以保持金额相同的两个对象,在排序之后的前后顺序不变.

下面开始学习这三种算法,先从冒泡算法开始吧!

冒泡算法(Bubble sort)

冒泡算法只会操作相邻的两个数据.冒泡算法每次都会对相邻的两个元素进行比较,看是否满足大小关系.如果不满足大小大小,就把他们的位置互换.一次冒泡,至少会让一个元素移动到他应该在的问题.重复了n次,就完成了 n个数据排序工作.举个例子,我们要对一组元素4,5,6,3,2,1按照从小到大进行排序.第一次冒泡操作的详细过程:

可以看出,经过一次冒泡操作之后,6 这个元素已经存储在正确的位置,我们要完成所有数据的排序,就要经过6次这样的操作.

当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用进行后续操作,举个例子,这里有6个元素,只需要四个冒泡操作就可以.

冒泡算法原理比较简单,下面我把代码贴出来:我用的oc语言


现在咱们可以结合之前讲的分析排序算法来思考三个问题:

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

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

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

为了保证冒泡算法的稳定系,当有相邻两个元素大小相等的时候,我们不做交换,所以冒泡算法是稳定的排序算法.

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

最好的情况,要排序的数据已经有序了,我们只进行一次冒泡操作,所以时间复杂度是O(n),最坏的情况是要排序的数据是倒序的,我们要进行n次冒泡操作,所以时间复杂度是O(n^2)

那么平均复杂度怎样计算呢?平均时间复杂度就是加权平均期望时间复杂度,分析的时候要结合概率论.

对于包含n个元素的数组,这n个元素就有n!(n的阶乘1*2*(n-2)*(n-1)*n)种排列方式 ,不同的排列方式,冒泡算法排序的时间也是不同的.比如我们前面举过的例子,其中一个要进行6次冒泡,另一个要4次,如果用概率论方法定量分析平均时间复杂度,涉及的数学推理和计算比较复杂,我们可以通过另一种思路,通过有序度逆序度来分析.

有序度是数组中具有有序 关系的元素对的个数,有序元素对用数学表达式表示就是这样,

有序元素对:a[i] <= a[j], 如果 i < j。

对于一个倒序排列的数组的有序度是0, 如:6,5,4,3,2,1,对于一个正序排列数组1,2,3,4,5,6 有序度是n*(n-1)/2,也就是15,我们把这种完全有序的数组的有序度称为满有序度.

逆序度的定位和有序度正好相反,逆序元素对:a[i] > a[j], 如果 i < j。

这三个概念可以得到一个公式,逆序度=满有序度-有序度.

比如前面那个冒泡算法的有序度对应关系就是:

冒泡排序包含两个操作原子,比较和交换。每交换一次,有序度就加 1。不管算法怎么改进,交换次数总是确定的,即为逆序度,也就是n*(n-1)/2–初始有序度。此例中就是 15–3=12,要进行 12 次交换操作。


插入排序(Insertion Sort)

一个有序的数组,我们往里面插入一个元素,怎么保证数据有序呢?我们只需要遍历数组,找到数据应该插入的地方将其插入.

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

如图所示,要排序的数据是 4,5,6,1,3,2,其中左侧为已排序区间,右侧为未排序区间.

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

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

为什么说移动次数就等于逆序度呢?我拿刚才的例子画了一个图表,你一看就明白了。满有序度是 n*(n-1)/2=15,初始序列的有序度是 5,所以逆序度是 10。插入排序中,数据移动的个数总和也等于 10=3+3+4。


代码展示:oc代码


大家也可以思考下那三个问题,1,插入排序是否是原地排序2,插入排序是否是稳定的排序,插入排序的时间复杂度.

选择排序(Selection Sort)


照例也是有三种算法需要你思考:前面已经有分析,这里直接公布结果:

首先,选择排序空间复杂度为 O(1),是一种原地排序算法。选择排序的最好情况时间复杂度、最坏情况和平均情况时间复杂度都为 O(n2)。

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

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

解答开头的问题

冒泡排序和插入排序的时间复杂度都是 O(n2),都是原地排序,为什么插入排序要比冒泡排序更受欢迎呢?

从代码实现上来看,冒泡排序的数据交换要比插入排序的数据移动要复杂的多,冒泡排序需要 3 个赋值操作,而插入排序只需要 1 个。

我们来看这段操作:

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

内容总结

要想分析、评价一个排序算法,需要从执行效率、内存消耗和稳定性三个方面来看。因此,这一节,我带你分析了三种时间复杂度是 O(n2) 的排序算法,冒泡排序、插入排序、选择排序。你需要重点掌握的是它们的分析方法。


参考: https://time.geekbang.org/column/article/41802

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 感赏爸爸煮好汤圆,态度温和叫儿子起床两次。 感赏爸爸关心我,说:你也把这碗汤圆吃了,并端到我面前。可妈妈象是应该的...
    云庆亲子教育阅读 165评论 0 0
  • 一、大米洗干净浸泡40分钟左右; 二、排骨洗干净,加酱油蒜蓉腌制一小时; 三、青菜洗干净备用; 四、把浸泡好的大米...
    芮至阅读 219评论 0 0
  • 零星飘落的雪花,给这个惨白、毫无生气的世界带来了一丝温情。 水滴石穿,水落成冰。 哈尔滨的冬,美的圣洁。像一块未经...
    小艾同学阅读 146评论 0 1