返回
JS算法之常规排序算法
前端
2023-09-29 22:30:21
前言
大家好,我是柒八九。最近在看Vue3源码分析,发现无论React还是Vue,在框架层面,为了实现特定的场景,它们为我们封装了很多比较复杂的逻辑。
比如,针对Virtual Dom的Diff算法,针对Promise实现异步编程等等。
当然,封装这些复杂逻辑的最终目的,其实是想让开发者的代码变的更简洁,便于管理和维护。
但是,作为一个对技术原理非常感兴趣的人,就想了解一下框架的底层到底是怎么实现的。
于是我,还是决定先从JavaScript的基础入手,比如,学习一些算法原理。
常规排序算法
今天我们聊一聊,排序算法。
排序算法,其实就是将一个无序的序列,通过某种算法转化为有序的序列,比如从小到大,或者从大到小。
排序算法,根据算法的时间复杂度,一般分为两大类:
- 时间复杂度为O(nlogn)的排序算法 :冒泡排序、选择排序、插入排序、希尔排序、归并排序、堆排序。
- 时间复杂度为O(n^2)的排序算法 :计数排序、桶排序、基数排序。
冒泡排序
冒泡排序,是一种最简单、最直观的排序算法。
冒泡排序的思路是:
- 从数组的第一个元素开始,依次与后面的元素比较,如果前一个元素大于后一个元素,则交换这两个元素的位置。
- 继续比较下一个元素,直到数组的最后一个元素。
- 然后,从数组的倒数第二个元素开始,依次与前面的元素比较,如果前一个元素大于后一个元素,则交换这两个元素的位置。
- 重复步骤2和步骤3,直到数组中的所有元素都按从小到大排列好。
冒泡排序的代码实现如下:
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
选择排序
选择排序,也是一种简单的排序算法。
选择排序的思路是:
- 从数组的第一个元素开始,找出数组中最小(或最大)的元素,并将其与第一个元素交换位置。
- 然后,从数组的第二个元素开始,找出数组中剩余元素的最小(或最大)的元素,并将其与第二个元素交换位置。
- 重复步骤2,直到数组中的所有元素都按从小到大排列好。
选择排序的代码实现如下:
function selectionSort(arr) {
for (let i = 0; i < arr.length - 1; 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;
}
插入排序
插入排序,也是一种简单的排序算法。
插入排序的思路是:
- 从数组的第二个元素开始,依次与前面的元素比较,如果前一个元素大于后一个元素,则将后一个元素插入到前一个元素的前面。
- 重复步骤1,直到数组中的所有元素都按从小到大排列好。
插入排序的代码实现如下:
function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
let j = i - 1;
let temp = arr[i];
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
return arr;
}
希尔排序
希尔排序,也是一种简单的排序算法。
希尔排序的思路是:
- 将数组分成若干个子数组,每个子数组的元素个数为h。
- 对每个子数组进行插入排序。
- 减少h的值,重复步骤1和步骤2,直到h=1。
希尔排序的代码实现如下:
function shellSort(arr) {
let h = 1;
while (h < arr.length / 3) {
h = 3 * h + 1;
}
while (h >= 1) {
for (let i = h; i < arr.length; i++) {
let j = i - h;
let temp = arr[i];
while (j >= 0 && arr[j] > temp) {
arr[j + h] = arr[j];
j -= h;
}
arr[j + h] = temp;
}
h = Math.floor(h / 3);
}
return arr;
}
归并排序
归并排序,是一种高效的排序算法。
归并排序的思路是:
- 将数组分成两半,分别对两半进行归并排序。
- 合并两个排好序的数组,得到一个排好序的数组。
归并排序的代码实现如下:
function mergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
let mid = Math.floor(arr.length / 2);
let left = mergeSort(arr.slice(0, mid));
let right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
let result = [];
let i = 0;
let 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++;
}
}
while (i < left.length) {
result.push(left[i]);
i++;
}
while (j < right.length) {
result.push(right[j]);
j++;
}
return result;
}
堆排序
堆排序,也是一种高效的排序算法。
堆排序的思路是:
- 将数组构建成一个堆。
- 从堆顶开始,依次与堆的最后一个元素交换位置,并调整堆的结构,使之仍然是一个堆。
- 重复步骤2,直到堆中只剩下一个元素。
堆排序的代码实现如下:
function heapSort(arr) {
buildMaxHeap(arr);
for (let i = arr.length - 1; i > 0; i--) {
let temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
maxHeapify(arr, 0, i);
}
return arr;
}
function buildMaxHeap(arr) {
for (let i = Math.floor(arr.length / 2) - 1; i >= 0; i--) {
maxHeapify(arr, i, arr.length);
}
}
function maxHeapify(arr, i, n) {
let largest = i;
let left = 2 * i + 1;
let right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
if (largest !== i) {
let temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
maxHeapify(arr, largest, n);