排序算法——冒泡排序

写在前面

排序算法是算法中最基础的,学好排序算法,在实际需求中还是很有用的,这里介绍几种常见的排序算法。

算法有性能好坏之分,这种判断的方式就是:

  • 时间复杂度(原本指一个算法执行所需的具体时间,但是在实际判断时一般指同一个语句执行次数最多的次数。)

  • 空间复杂度(原本指一个算法在内存中所占用的存储空间,但在实际判断时一般指用到的临时变量的个数,如果是固定的常量数就是 O(1),若是不固定和 n 有关,则就是 n 的函数)

在排序算法中,还多了一个特殊的判断依据:

  • 稳定性
  1. 如果 a=b,排序前是 a 在 b 的前面,排序后 a 还是在 b 的前面,那么就称这种算法是稳定的

  2. 如果 a=b, 排序前 a 在 b 的前面,排序后可能会 b 在 a 的前面,那么就称这种算法是不稳定的

排序算法可以可以映射到生活中。我们日常的排队时,安排从低到高的顺序排,我们人眼是一眼就可以看出来谁高些就让其往后站,谁低些就让其往前站。但计算机不能,它只能按照告诉它的逻辑去执行。

下面就看程序员是如何告诉计算机进行操作的。

1. 冒泡排序

冒泡排序是最基础的排序算法,该算法的具体思路就是从第一个开始,依次往后两两比较,二者中谁高就往后站,直至比到最后一个人,此时,最后一个人就是整个队中最高的。然后再从头开始依次往后两两比较,二者中谁高谁站后面,此时因为最后一个人已经是最高的了,因此就直至比到倒数第二个,依次类推。每比一趟都会确定一个人的最终占位,一共比 n-1 趟就能确定 n-1 个人的位置了,那剩下那一个人的位置就是在第一个了。

依据上述思路,使用 js 实现代码如下:

function sort(arr){
    for(let i = 0; i < arr.length - 1; i++)  /*第一个for循环确保是 n-1 趟*/
        for(let j = 0; j < arr.length -1 - i; j++){  /*第二个for循环是比较的起始点和末点,起始点一直是0,末点随着 i 的变化而变化*/
            if(arr[j] > arr[j+1]){   /*因为这里是有用到 j+1, 所以上层循环的末点要到 length -1 - i,因为当 i 为 0 时,末点最远为 < length - 1,那 j+1 的位置才是 length - 1。注意不要地址越界了*/
                let temp = arr[j]  
                arr[j] = arr[j+1]
                arr[j+1] = temp
            }
        }
    return arr
}

这里没有规定 i 必须从 0 开始,只要控制第一个循环为 n-1 趟就可以,第二层循环根据 i 值具体计算。只要注意 j+1 没有地址越界就可以,如下:

function sort2(arr){
    for(let i = 1; i < arr.length; i++)
        for(let j = 0; j < arr.length - i; j++){
            if(arr[j] > arr[j+1]){
                let temp = arr[j]
                arr[j] = arr[j+1]
                arr[j+1] = temp
            }
        }
    return arr
}

平均时间复杂度:O(n2),最好时间复杂度:O(n),最差时间复杂度:O(n2)

空间复杂度:O(1)

稳定性:稳定

1.1 改进的冒泡排序

在上述最基本的冒泡排序中,如果一个数组本身就是有序的,或者比较不到 n-1 趟时就已经是有序的了,那么对于上述的排序算法,会一直执行比较语句 n 次,直到比了 n-1 趟为止。

但实际上,如果一趟下来,交换语句从没有执行过,那就说明这个数组已经是有序的数组了,没有必要再进行后续的循环比较了,因此在每趟中加个判断条件改进冒泡排序如下:

function sort3(arr){
    for(let i = 0; i < arr.length - 1; i++){
        let flag = false
        for(let j = 0; j < arr.length - 1 -i; j++){
            if(arr[j] > arr[j+1]){
                flag = true
                let temp = arr[j]
                arr[j] = arr[j+1]
                arr[j+1] = temp
            }
        }
        if(!flag)
            break
    }
    return arr
}

改进后的冒泡算法的时间复杂度成了一个变量,根据比较数组的有序程序有不同的时间复杂度,因此,此时就不再使用时间复杂度来衡量了,而是使用平均时间复杂度,就是将最差情况的事件复杂度和最好情况的复杂度进行平均的计算。

最好情况下,这个数组本身就是有序的,那语句都会只被执行一遍,时间复杂度为 O(1)。最坏情况下,这个数组是完全逆序的,n-1 趟一趟也不能少,此时时间复杂度为 O(n^2),因此取平均数还是 O(n^2)

平均时间复杂度:O(n2),最好时间复杂度:O(1),最坏时间复杂度:O(n2)

空间复杂度:O(1)

稳定性:稳定

后续会更新冒泡排序的其他改进方式。。。

原文地址:https://www.cnblogs.com/lovevin/p/13599197.html