冒泡排序的时间复杂度在最坏情况下是O(n^2),因为有两个嵌套的循环。空间复杂度为O(1),只需要一个临时变量用于交换。冒泡排序的主要优点是算法简单,但是它的效率较低,通常只适用于小规模的排序。对于大数据量,会有更优的算法选择,如快速排序等。
选择排序
function selectionSort(arr) {
for (let i = 0; i < arr.length; i++) {
let minIndex = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
let temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
let arr = [5, 3, 6, 2, 10];
console.log(selectionSort(arr)); // [2, 3, 5, 6, 10]
选择排序是一种简单直观的排序算法。它的工作原理是:
- 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
- 重复第二步,直到所有元素均排序完毕。
举例:
原数组:[5, 3, 6, 2, 10]
- 第一轮:将3与5交换位置,得到[3, 5, 6, 2, 10]
- 第二轮:将2与5交换位置,得到[3, 2, 6, 5, 10]
- 第三轮:将2与6交换位置,得到[2, 3, 6, 5, 10]
可以看出,选择排序通过找到未排序的最小值,放到已排序序列的末尾,经过n-1轮后得到有序序列。
选择排序时间复杂度为O(n^2),因为需要两层循环来选择最小值。空间复杂度为O(1)。
选择排序由于简单, Performance也比冒泡排序好,但仍是O(n^2)的复杂度。它适合对小规模数组排序。对于大数据量,也有更优的排序算法选择。
选择排序的主要优点是比较次数少,对于大规模数组,交换所需时间少,然而步骤单一,各轮排序并无交叉进行,速度较慢。可以适当优化算法提高速度。
插入排序
function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
let curr = arr[i];
let j = i - 1;
while (j >= 0 && curr < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = curr;
}
return arr;
}
let arr = [5, 2, 4, 6, 1, 3];
console.log(insertionSort(arr)); // [1, 2, 3, 4, 5, 6]
插入排序的工作原理是,将数组分为两个部分,前面部分是有序的,后面部分是无序的。每次从无序部分取出第一个元素,并把它插入到有序部分中的正确位置。
插入排序包含以下步骤:
- 从第一个元素开始,该元素可以认为已经被排序。
- 取出下一个元素,在已排序的元素序列中从后向前扫描。
- 如果该元素(已排序)大于新元素,将该元素移到下一位置。
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
- 将新元素插入到该位置后。
- 重复步骤2~5。
例如对数组[5,2,4,6,1,3] 排序:
- 第一轮:取出2,插入有序序列[5],变为 [2,5]
- 第二轮:取出4,插入有序序列[2,5],变为[2,4,5]
- 第三轮:取出6,插入有序序列[2,4,5],变为[2,4,5,6]
- 第四轮:取出1,插入有序序列[2,4,5,6],变为[1,2,4,5,6]
- 第五轮:取出3,插入有序序列[1,2,4,5,6],变为[1,2,3,4,5,6]
可以看出,插入排序逐步构建有序序列,对于少量元素的数组,插入排序能够有效减少交换和移动次数。
插入排序的时间复杂度是O(n^2),空间复杂度为O(1)。相比冒泡排序和选择排序,插入排序能够更快地对部分有序数组进行排序。因此适合对小规模数组排序。
快速排序
function quickSort(arr, left = 0, right = arr.length - 1) {
if (left < right) {
let pivotIndex = partition(arr, left, right);
quickSort(arr, left, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, right);
}
return arr;
}
function partition(arr, left, right) {
let pivot = arr[Math.floor((left + right) / 2)];
while (left <= right) {
while (arr[left] < pivot) {
left++;
}
while (arr[right] > pivot) {
right--;
}
if (left <= right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
}
return left;
}
let arr = [5, 2, 4, 6, 1, 3];
quickSort(arr);
console.log(arr); // [1, 2, 3, 4, 5, 6]
快速排序使用分治策略来把一个串行(list)分为两个子串行(sub-lists)。具体算法步骤如下:
- 从数列中挑出一个元素,称为 “基准”(pivot)
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
例如序列:[5, 3, 8, 4, 1, 7, 2, 6]
选择5为基准,第一次分区后变为:[3, 2, 1, 4, 5, 7, 8, 6],5处于中间位置
接着递归排序左右两个子序列。
快速排序利用了分治策略,能够快速排序,时间复杂度为O(nlogn),是非常高效的排序算法。但空间复杂度较高,为O(nlogn),需要额外空间recursion调用。
快速排序是原地排序,不需要额外空间,并且时间效率很高,适合排序大数据量。是最常用的排序算法之一。
✨ 结语
总结来说,排序算法是计算机科学中一个非常基础和经典的问题。本文详细介绍和分析了几种JavaScript中的排序算法实现,包括冒泡排序、选择排序、插入排序和快速排序,通过示例代码阐明了各类排序的原理和步骤。
不同的排序算法根据其时空复杂度的不同,都有其应用场景。一般来说,简单排序如冒泡排序和选择排序时间复杂度较高,仅适用于小规模数组;而插入排序和快速排序更适合对大数据量的数组进行排序。
总结
大厂面试问深度,小厂面试问广度,如果有同学想进大厂深造一定要有一个方向精通的惊艳到面试官,还要平时遇到问题后思考一下问题的本质,找方法解决是一个方面,看到问题本质是另一个方面。还有大家一定要有目标,我在很久之前就想着以后一定要去大厂,然后默默努力,每天看一些大佬们的文章,总是觉得只有再学深入一点才有机会,所以才有恒心一直学下去。

1538

被折叠的 条评论
为什么被折叠?



