冒泡排序
(最常用,也是是原理最简单的排序,但是他是三种排序算法中效率最低的, 适用于数据量很小的排序场景,因为冒泡原理简单)
时间复杂度O(n*n),可以从前向后,也可以从后向前进行排序.
案例解析:每一轮把最大的数放到最后面
//冒泡排序
let arr = [2, 4, 1, 6, 3]
function bubbled(arr) {
for (let i = 0; i < arr.length - 1; i++) {
//【!!注意】这里不是j=i,因为回回都必须重头遍历,才能不漏一个,不然会有BUG
for (let j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
console.log(bubbled(arr)); //[1,2,3,4,6]
选择排序
(复杂度O(n*n),但是他的数据交换时间复杂度仅为O(N),在数组排序过程中,数据的移动交换是比较耗时的,所以相对于冒泡排序而言,选择排序的效率大大提高。 适用于大多数排序场景,虽然他的对比次数较多,但是数据量大的时候,他的效率明显优于冒泡)
顾名思义,选择出来进行排序,最开始先遍历一遍数组,选择出来最小的值放在数组第一个位置,之后再对第二个元素之后的元素进行遍历,选择出来最小的值放在这个子无序序列的第一位,也就是数组的第二个元素,这样前两个数就排完了,以此类推,
//选择排序(指挨个挨个遍历,`选择`右侧最小与之交换)
let arr1 = [2, 4, 1, 6, 3]
function select(arr1) {
for (let i = 0; i < arr1.length - 1; i++) {
let min = i //保存右侧最小值的下标
for (let j = i + 1; j < arr1.length; j++) {
if (arr1[j] < arr1[min]) {
//【!!注意】:这里每次循环保存相对较小值的下标
min = j
}
}
if (arr1[min] < arr1[i]) {
let temp;
temp = arr1[min]
arr1[min] = arr1[i]
arr1[i] = temp
}
}
return arr1
}
console.log(select(arr1)); //[1,2,3,4,6]
插入排序
(插入排序适用于已有部分数据有序的情况,有序部分越大越好。)
插入排序复杂度也是O(n*n),涉及到了元素的移动。先把数组中的第一个元素看成有序数列,查找与这个有序数列相邻元素,与有序数列中的每个元素进行比较,插入到合适的位置,直至有序数列中的元素和原数组元素相等排序完成。
//插入排序
let arr2 = [2, 4, 1, 6, 3]
function Ins(arr2) {
let temp //专门用于保存作为比较的i项
for (let i = 1; i < arr2.length; i++) {
while (i > 0 && arr2[i] < arr2[i - 1]) {
temp = arr2[i] //先对后面的值保存一下,因为这个值还要跟前面做对比啊! 你看下一步就要把后面的值覆盖了
arr2[i] = arr2[i - 1]
arr2[i - 1] = temp
i--;
}
}
return arr2
}
console.log(Ins(arr2)) //[1,2,3,4,6]
快速排序
(快速排序对于数组元素重复率高的不适用。)
快速排序要求选择的枢轴值两边子序列长度最好一致,快速排序的时间消耗上主要在对元素的划分上,元素数量为k的数组,共需要比较k-1次。快速排序主要在于选择基准,如果选择的基准刚好是最小的或者最大的元素,那么这时候时间复杂度最大为O(n*n),如果刚好选择的基准就是这个无序序列的中值,时间复杂度最小为O(nlgn),总的关键字比较次数:O(nlgn)
快速排序为社么快,这里要提到一个分治思想,分而治之,虽然用到的是递归,虽然也有最坏情况下和冒泡,选择排序相等的时间复杂度,但是,分治思想是关键,把一个大问题分成两个小问题,分别同时处理,这两个小问题再向下分,最后问题也就水落石出了,分治思想避免了做无用功,选择一个基准,大于他的数在他的右面,小于他的数在他的左面,在进行比较的时候,左面的数只在左面进行比较就可以,没有必要再去右面比较,这样节省了时间
let arr3 = [2, 4, 1, 6, 3]
function fast(arr3) {
if (arr3.length <= 1) return arr3; //【!!!注意】:这一句必须加上,因为有时候递归回来可能左边数组只有一个元素(或空数组),这时直接返回arr ,结束left递归,让它去递归right
let left = [];
let right = [];
let pivot = arr3[0]
for (let i = 1; i < arr3.length; i++) {
if (arr3[i] < pivot) {
left.push(arr3[i])
} else {
right.push(arr3[i])
}
}
return fast(left).concat([pivot], fast(right))
}
console.log(fast(arr3)); //[1,2,3,4,6]
测试时可采用我前期博文调试前端console.time和console.timeEnd_如花菇凉的博客-CSDN博客来监测对比性能时间度
以上参考文献
【总结】:大厂面试常考手撕代码 —— JavaScript排序算法(冒泡排序、选择排序、插入排序、快速排序)_故里有长安丶丶的博客-CSDN博客