数据结构与算法笔记day08:排序(冒泡排序|插入排序|选择排序)

        排序算法非常多,这里我们只学习众多排序算法中最经典、最常用的一小部分:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。

    1如何分析一个“排序算法”?       

    (1)排序算法的执行效率

        对于排序算法执行效率的分析,我们一般会从这几个方面来衡量:

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

        对于要排序的数据,有的接近有序,有的完全无序,有序度不同的数据,对于排序的执行时间肯定是有影响的,我么要知道排序算法在不同数据下的性能表现。

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

        在实际的软件开发中,我们排序的可能是规模很小的数据,所以在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。

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

    (2)排序算法的内存消耗

        排序算法的内存消耗也可以通过空间复杂度来衡量,不过针对排序算法的空间复杂度,我们引入了一个新的概念:原地排序原地排序算法,就是特指空间复杂度是O(1)的排序算法。

    (3)排序算法的稳定性

        针对排序算法,我们还有一个重要的度量指标:稳定性。这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。   

        比如,有一组数据:5,7,2,1,5,8,按大小排序后为:1,2,5,5,7,8。这组数据里面有两个5,经过某种排序算法排序之后,如果两个5的前后顺序没有改变,那我们就把这种排序算法叫做稳定的排序算法,如果前后顺序发生变化,那对应的排序算法就叫做不稳定的排序算法

    2冒泡排序

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

        下面用一个例子来看看冒泡排序的整个过程。对一组数据:4,5,6,3,2,1,从小到大进行排序,第一次冒泡操作的详细过程如下:

        经过依次冒泡操作之后,6这个元素已经存储在正确的位置上了。要想完成所有数据的排序,我们只要进行6次这样的冒泡操作就行了。

        如下图所示:

        刚刚冒泡的过程还可以优化:当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作。

        下面的例子给6个元素排序,只需要4次冒泡操作就可以了:

        冒泡排序的代码:

        运行结果:

        总结三点:

        第一,冒泡排序是原地排序算法。因为冒泡的过程指设计相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为O(1),是原地排序算法。

        第二,冒泡排序是稳定的排序算法。在冒泡排序中,只有交换才可以改变两个元素的前后顺序,为了保证冒泡排序算法的稳定性,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法。

        第三,冒泡排序的最好情况时间复杂度是O(n),最坏情况时间复杂度是O(n^2),平均情况时间复杂度是O(n^2)。要排序的数据已经是有序的是最好的情况,我们只需要进行一次冒泡操作。要排序的数据刚好是倒序排列的是最坏的情况,我们需要进行n次冒泡操作。

        这里来说一下平均时间复杂度的推导方法,这个方法其实并不严格,但是比较实用。我们引入有序度的概念,有序度就是有序的元素对的个数:

        比如:

        对于一个倒序排列的数组,有序度是0;对于一个完全有序的数组,有序度就是n*(n-1)/2,我们把这种完全有序的数组的有序度叫做满有序度

        逆序度的定义跟有序度正好相反:

        这三个概念间还存在一个公式:逆序度=满有序度-有序度

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

        冒泡排序包含两个操作原子:比较交换。每交换依次,有序度就加1,而交换的次数总是确定的,即为逆序度,也就是n*(n-1)/2-初始有序度

        而最坏情况下的交换次数是n*(n-1)/2,最好情况下的交换次数是0,我们取个中间值,即为n*(n-1)/4。比较操作肯定比交换操作多,而复杂度的上限是O(n^2),所以平均情况下的时间复杂度就是O(n^2)

    3插入排序

        我们先来看看插入排序的思想。

        一个有序的数组,我们往里面添加一个新的数据,为了保持有序,我们需要遍历数组,找到数据应该插入的位置再将它插入:

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

        一个例子:

        插入排序包含两个操作:元素的比较元素的移动,通过比较找到插入位置,然后将插入点后的元素顺序往后移动一位,腾出位置给元素插入。

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

        下图中例子的满有序度为n*(n-1)/2=15,初始序列的有序度是5,逆序度是10,移动次数为3+3+4=10:

        插入排序的代码(写的过程中捋了好几次才捋顺,以后再写几遍!):

        运行结果:

        总结一下:

        第一,插入排序是原地排序算法。它并不需要额外的存储空间,空间复杂度是O(1)。

        第二,插入排序是稳定的排序算法。对于值相同的元素,我们选择将后面出现的元素插入到前面出现元素的后面,这样就保持了原有的前后顺序不变。

        第三,插入排序的最好时间复杂度为O(n),最坏时间复杂度是O(n^2),平均时间复杂度是O(n^2)。

    4选择排序

        选择排序的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。

        选择排序原理示意图:

        直接简单总结一下:

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

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

        第三,选择排序是不稳定的排序算法。由上面图示的过程可以看出,选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。比如5,8,5,2,9这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素2,与第一个5交换位置,那第一个5和中间的5的顺序就变了,所以就不稳定了。

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

        选择排序是不稳定的排序算法,所以比冒泡排序和插入排序要逊色。

        插入排序和冒泡排序的时间复杂度都是O(n^2),都是原地排序算法,而且都是稳定的排序算法。那么为什么插入排序比冒泡排序更受欢迎呢?

        前面有讲到,冒泡排序和插入排序不管怎么优化,元素交换的次数是一个固定值,是原始数据的逆序度。但是,从代码实现上来看,冒泡排序的数据交换要比插入排序的数据移动要复杂,冒泡排序需要3个赋值操作,而插入排序只需要1个,如下图所示:

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

        因此,虽然他们的时间复杂度是一样的,但是如果我们希望把优化做到极致,肯定首选插入排序。插入排序也可以进行优化,感兴趣的话可以学习一下希尔排序

    6小结

        要想分析、评价一个排序算法,需要从执行效率、内存消耗、稳定性三个方面来看。

        这三种时间复杂度为O(n^2)的排序算法中,冒泡排序、选择排序我们更多的是了解它的理论,实际开发中应用并不多,插入排序还是挺有用的,有些编程语言中的排序函数的实现原理会用到插入排序算法(优化后的)。

        这三种排序算法对于小规模数据的排序非常高效,但是大规模数据排序的时候,时间复杂度还是稍微有点高,下一节我们会讲更适用于大规模数据的时间复杂度为O(nlogn)的排序算法~

        戳这里查看源代码。

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

推荐阅读更多精彩内容