JavaScript常用的算法详解
1. 排序算法
1.1. 冒泡排序(Bubble Sort)
冒泡排序是一种简单的排序算法,通过反复遍历数组,将相邻的两个元素进行比较并交换位置,从而将最大的元素逐步移到数组的末尾。
优点:
-
实现简单:算法逻辑简单,易于理解。
-
原地排序:不需要额外的内存空间,空间复杂度为 O(1)。
缺点:
-
效率低下:时间复杂度为 O(n²),对大规模数据排序效率很差。
-
适合小规模数据:对于小规模数据或几乎有序的数据有一定效果。
代码示例:
function bubbleSort(arr: number[]): number[] {const len = arr.length;for (let i = 0; i < len; i++) {for (let j = 0; j < len - i - 1; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // 交换位置}}}return arr;
}
使用场景:
-
小规模数据排序:当数据量很小时可以使用,尤其是数据几乎有序时。
1.2. 选择排序(Selection Sort)
选择排序每次从未排序的部分选择最小或最大的元素,放到已排序部分的末尾,逐步缩小未排序部分。
优点:
-
简单直观:逻辑简单,易于实现。
-
数据移动次数少:每次交换只涉及一次最小值移动。
缺点:
-
效率较低:时间复杂度为 O(n²),大规模数据表现不佳。
-
不稳定排序:相同元素的相对顺序可能会被改变。
代码示例:
function selectionSort(arr: number[]): number[] {const len = arr.length;for (let i = 0; i < len - 1; i++) {let minIndex = i;for (let j = i + 1; j < len; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}if (minIndex !== i) {[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; // 交换最小值到已排序部分}}return arr;
}
使用场景:
-
小规模数据排序:适合数据量较少的情况。
-
对内存写操作敏感的场景:如硬盘排序或闪存存储。
1.3. 插入排序(Insertion Sort)
插入排序通过将每个元素插入到已排序部分的适当位置,逐步构建有序序列。
优点:
-
适合小规模数据:表现较好,尤其是数据接近有序时。
-
稳定排序:相同元素的相对顺序不会改变。
缺点:
-
效率较低:时间复杂度为 O(n²),无序数据时性能较差。
代码示例:
function insertionSort(arr: number[]): number[] {const len = arr.length;for (let i = 1; i < len; i++) {let key = arr[i];let j = i - 1;while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j]; // 将比 key 大的元素后移j--;}arr[j + 1] = key;}return arr;
}
使用场景:
-
小规模数据排序:适合数据量较少的情况。
-
几乎有序的数据:当数据接近有序时,插入排序的时间复杂度接近 O(n)。
1.4. 归并排序(Merge Sort)
归并排序是一种基于分治思想的排序算法,将数组分为两个子数组,递归排序后合并成一个有序数组。
优点:
-
时间复杂度稳定:O(n log n),适合大规模数据。
-
稳定排序:相同元素的相对顺序不变。
缺点:
-
需要额外空间:需要 O(n) 的额外空间存储合并结果。
代码示例:
function mergeSort(arr: number[]): number[] {if (arr.length <= 1) return arr;const mid = Math.floor(arr.length / 2);const left = mergeSort(arr.slice(0, mid));const right = mergeSort(arr.slice(mid));return merge(left, right);
}function merge(left: number[], right: number[]): number[] {const result: number[] = [];let i = 0, j = 0;while (i < left.length && j < right.length) {if (left[i] < right[j]) {result.push(left[i]);i++;} else {result.push(right[j]);j++;}}return result.concat(left.slice(i), right.slice(j));
}
使用场景:
-
大规模数据排序:归并排序适合处理大量数据。
-
链表排序:归并排序在链表上的效率高于其他排序算法。
1.5. 快速排序(Quick Sort)
快速排序通过选择一个基准元素,将数组分为小于和大于基准的两部分,递归排序。
优点:
-
平均性能优异:时间复杂度为 O(n log n),常数因子小,适合大规模数据。
-
原地排序:不需要额外的存储空间,空间复杂度为 O(log n))。
缺点:
-
最坏情况退化:在不好的情况下,时间复杂度会退化为 O(n²)。
-
不稳定排序:相同元素的相对顺序可能改变。
代码示例:
function quickSort(arr: number[]): number[] {if (arr.length <= 1) return arr;const pivot = arr[Math.floor(arr.length / 2)];const left = arr.filter(x => x < pivot);const right = arr.filter(x => x > pivot);const middle = arr.filter(x => x === pivot);return quickSort(left).concat(middle, quickSort(right));
}
使用场景:
-
大规模数据排序:快速排序是实际应用中常用的排序算法之一。
-
对空间要求敏感的场景:快速排序为原地排序。
1.6. 希尔排序(Shell Sort)
希尔排序是插入排序的改进版,通过将数组按照一定的间隔分为多个子序列,先对每个子序列进行插入排序,然后逐渐减小间隔直到最后对整个序列进行插入排序。
优点:
-
改进了插入排序:通过间隔分组,减少了大数据量的移动,提升了效率。
-
适合中等规模数据