写在前面
排序算法是算法中最基础的,学好排序算法,在实际需求中还是很有用的,这里介绍几种常见的排序算法。
算法有性能好坏之分,这种判断的方式就是:
-
时间复杂度(原本指一个算法执行所需的具体时间,但是在实际判断时一般指同一个语句执行次数最多的次数。)
-
空间复杂度(原本指一个算法在内存中所占用的存储空间,但在实际判断时一般指用到的临时变量的个数,如果是固定的常量数就是 O(1),若是不固定和 n 有关,则就是 n 的函数)
在排序算法中,还多了一个特殊的判断依据:
- 稳定性
-
如果 a=b,排序前是 a 在 b 的前面,排序后 a 还是在 b 的前面,那么就称这种算法是稳定的
-
如果 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)
稳定性:稳定
后续会更新冒泡排序的其他改进方式。。。