一文领略 冒泡、选择、插入排序

111 阅读2分钟

大家好呀, 我是小九九的爸爸,我给自己立了个flag,要在8月前写完10大排序算法,这不,前些天写完了冒泡排序、选择排序、插入排序。这次决定将这3个算法合入到一篇文章了,加深一下它们的理解。

一、关联性

这三个算法都是基于比较原地排序算法,具体如下:

算法如何比较
冒泡2次遍历,每次遍历都要确定本轮数据中的最大值,并将最大值依次放到数组的最后一位
选择2次遍历,每次遍历都要确定本轮数据中的最小值,并将最小值依次放到数组的第一位
插入2次遍历,从第2项开始,依次比较之前的数据,直到将当前项插入到所有前项里的合适位置

二、代码实操

2.1、冒泡(从小大)

中心思想:2次遍历,每次遍历都要确定本轮数据中的最大值,并将最大值依次放到数组的最后一位

这里有个问题,而且我认为比较重要,为什么要二次遍历?

我们试一下只用一次遍历,且原地改变的方式,能不能完成数据的排序。

let arr = [...]

for (let index = 0; index < ar.length; index++){
    if (arr[index] > arr[index+1]){
        [ arr[index], arr[index+1] ] = [ arr[index+1], arr[index] ];
    }
}

return arr

上面这种写法,只能得出最大值,但是无法得出第二大值,第三大值,因此无法给数组排序。

所以我们才需要2层遍历,最外层的遍历就是为了得出第N大值。最内层的遍历就是为了得出每一轮的最大值。

let arr = [...];

// 其实难点在于为什么要二次遍历,上面已经解释过了
for (let x = 0; x < arr.length; x++){
    for (let y = 0; y < arr.length - x; y++){
        if (arr[y] > arr[y+1]){
            // 发生交换
            [ arr[y], arr[y+1] ] = [ arr[y+1], arr[y] ];
        }
    }
}

return arr;

2.2、选择(从小到大)

中心思想:2次遍历,每轮遍历都要得出本轮的最小值,并将最小值分别放到前面

其实选择排序就是冒泡排序的逆向流程。双层遍历的目的跟冒泡一样,一层遍历只能确定最小值,但是不能确定第N小值

let data = [...];

for (let x = 0; x < data.length; x++){
    let minIndex = x;
    for (let y = minIndex + 1; y < data.length; y++){
        if (data[y] < data[minIndex]){
            minIndex = y;
        }
    }
    [ data[x], data[minIndex] ] = [ data[minIndex], data[x] ];
}

return data;

2.3、插入(从小到大)

中心思想:从第二项开始,将本数据项与前面的数据进行比较,直到插入合适的位置

因为我们的最终想要的数据是从小到大排序,所以什么是合适的位置呢?

其实就是找到自己是第几小值而已。

let data = [...];

for (let x = 1; x < data.length; x++){
    let curIndex = x;
    while(curIndex - 1 >= 0 && data[curIndex] < data[curIndex - 1]){
        [ data[curIndex], data[curIndex - 1] ] = [ data[curIndex - 1], data[curIndex] ];
        curIndex--;
    }
}

return data;

三、最后

好啦,本次排序文章就分享到这啦,大家会看到我并没有分析这3种排序的优缺点,是因为我觉着优缺点是针对场景而言的,而这三种排序在遇到最坏的情况下,时间复杂度都是O(N^2)。也就是说,对这三种排序而言,没有银弹,一种适用,另外2种必然也满足要求。

那么,我们下期再见啦。希望我说的对你有帮助~~