09|排序(上):为什么插入排序比冒泡排序更受欢迎?

image.png

一、几种经典排序算法及其时间复杂度级别

冒泡、插入、选择 O(n^2) 基于比较
快排、归并 O(nlogn) 基于比较
计数、基数、桶 O(n) 不基于比较


image.png

二、如何分析一个排序算法?

1.学习排序算法的思路?明确原理、掌握实现以及分析性能。

2.如何分析排序算法性能?从执行效率内存消耗以及稳定性3个方面分析排序算法的性能。

3.执行效率:从以下3个方面来衡量
1)最好情况、最坏情况、平均情况时间复杂度
2)时间复杂度的系数、常数、低阶:排序的数据量比较小时考虑
3)比较次数和交换(或移动)次数

4.内存消耗:通过空间复杂度来衡量。针对排序算法的空间复杂度,引入原地排序的概念,原地排序算法就是指空间复杂度为O(1)的排序算法。

5.稳定性:如果待排序的序列中存在值等的元素,经过排序之后,相等元素之间原有的先后顺序不变,就说明这个排序算法时稳定的。

三、冒泡排序

1.排序原理

1)冒泡排序只会操作相邻的两个数据。
2)对相邻两个数据进行比较,看是否满足大小关系要求,若不满足让它俩互换。
3)一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完成了n个数据的排序工作。
4)优化:若某次冒泡不存在数据交换,则说明已经达到完全有序,所以终止冒泡。


image.png

2.代码实现

// 冒泡排序
function bubblesort(arr) {
  let num = 0;
  len = arr.length;
  if (len < 1) return;
  for (let i = 0; i < len; ++i) {
    let flag = false;
    for (let j = 0; j < len - i; ++j) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        num++;
        flag = true;
      }
    }
    if (!flag) break;
  }
  return {
    arr,
    num,
  };
}

console.log(bubblesort([4, 5, 6, 3, 2, 1]));

3.性能分析

1)执行效率:最小时间复杂度、最大时间复杂度、平均时间复杂度

      最小时间复杂度:数据完全有序时,只需进行一次冒泡操作即可,时间复杂度是O(n)。

      最大时间复杂度:数据倒序排序时,需要n次冒泡操作,时间复杂度是O(n^2)。
平均时间复杂度:通过有序度和逆序度来分析。

什么是有序度?

      有序度是数组中具有有序关系的元素对的个数,比如[2,4,3,1,5,6]这组数据的有序度就是11,分别是[2,4][2,3][2,5][2,6][4,5][4,6][3,5][3,6][1,5][1,6][5,6]。同理,对于一个倒序数组,比如[6,5,4,3,2,1],有序度是0;对于一个完全有序的数组,比如[1,2,3,4,5,6],有序度为n*(n-1)/2,也就是15,完全有序的情况称为满有序度。

什么是逆序度?逆序度的定义正好和有序度相反。核心公式:逆序度=满有序度-有序度。

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

      冒泡排序包含两个操作原子,即比较和交换,每交换一次,有序度加1。不管算法如何改进,交换的次数总是确定的,即逆序度。

      对于包含n个数据的数组进行冒泡排序,平均交换次数是多少呢?最坏的情况初始有序度为0,所以要进行n(n-1)/2交换。最好情况下,初始状态有序度是n(n-1)/2,就不需要进行交互。我们可以取个中间值n*(n-1)/4,来表示初始有序度既不是很高也不是很低的平均情况。

      换句话说,平均情况下,需要n*(n-1)/4次交换操作,比较操作肯定比交换操作多,而复杂度的上限是O(n^2), 所以平均情况时间复杂度就是O(n^2)。
以上的分析并不严格,但很实用,这就够了。

2)空间复杂度:每次交换仅需1个临时变量,故空间复杂度为O(1),是原地排序算法。
3)算法稳定性:如果两个值相等,就不会交换位置,故是稳定排序算法。

四、插入排序

1.算法原理

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


image.png

2.代码实现

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


image.png
function insertionSort(arr){
    let len = arr.length
    if(len <= 1) return

    for(let i = 1; i < len; ++i){
        let curValue = arr[i],
            j = i - 1;
        
        for(; j >= 0; --j){
            if(arr[j] > curValue){
                arr[j + 1] = arr[j] // 数据移动
            } else {
                break
            }
        }
        arr[j+1] = curValue
    }
    return arr
}

console.log(insertionSort([4,5,6,1,3,2]))

3.性能分析

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

      如果要排序的数组已经是有序的,我们并不需要搬移任何数据。只需要遍历一遍数组即可,所以时间复杂度是O(n)。如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,因此时间复杂度是O(n^2)。 而在一个数组中插入一个元素的平均时间复杂都是O(n),插入排序需要n次插入,所以平均时间复杂度是O(n^2)。

2)空间复杂度:从上面的代码可以看出,插入排序算法的运行并不需要额外的存储空间,所以空间复杂度是O(1),是原地排序算法。
3)算法稳定性:在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现的元素的后面,这样就保持原有的顺序不变,所以是稳定的。

五、选择排序

1.算法原理

      选择排序算法也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,并将其放置到已排序区间的末尾。


image.png

2.代码实现(见下一条留言)

3.性能分析

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

      选择排序的最好、最坏、平均情况时间复杂度都是O(n^2)。为什么?因为无论是否有序,每个循环都会完整执行,没得商量。

2)空间复杂度:

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

3)算法稳定性:

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

六、思考

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

   冒泡排序移动数据有3条赋值语句,而选择排序的交换位置的只有1条赋值语句,因此在有序度相同的情况下,冒泡排序时间复杂度是选择排序的3倍,所以,选择排序性能更好。

2.如果数据存储在链表中,这三种排序算法还能工作吗?如果能,那相应的时间、空间复杂度又是多少呢?

   一般而言,考虑只能改变节点位置,冒泡排序相比于数组实现,比较次数一致,但交换时操作更复杂;插入排序,比较次数一致,不需要再有后移操作,找到位置后可以直接插入,但排序完毕后可能需要倒置链表;选择排序比较次数一致,交换操作同样比较麻烦。综上,时间复杂度和空间复杂度并无明显变化,若追求极致性能,冒泡排序的时间复杂度系数会变大,插入排序系数会减小,选择排序无明显变化。

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