目录
- 一、综合分析
- 二、冒泡排序
- 三、选择排序
- 四、插入排序
- 五、归并排序
- 五、快速排序
- 六、希尔排序
- 七、堆排序
- 八、计数排序
- 九、桶排序
- 十、基数排序
算法是程序开发中不可或缺的一部分。排序算法作为最基本、最常用的算法之一,在程序开发中起到了至关重要的作用。本文将深入探讨十大经典排序算法,探索这些排序算法的实现原理、时间复杂度及其适用场景并使用TypeScript语言来实现。废话不多说,让我们一同踏上 TypeScript排序算法实战的旅程!
冒泡排序是一种基本的排序算法,其思路是比较相邻元素的大小,如果逆序则交换,一轮结束后将最大元素“冒泡”到数列的末尾。这个过程像冒泡一样,因此被称为冒泡排序。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
const bubblesort = (arr) => {
for (let i = 0; i < arr.length; i++){
for (let j = 0; j < arr.length - i; j++){
if(arr[j] > arr[j + 1])
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
return arr;
}
console.log(bubblesort([4, 2, 6, 1, 5, 8, 7, 3]));
时间复杂度:
选择排序是一种简单直观的排序算法,其思想是每次从待排序的数列中选择最小(或最大)的元素,放到已排序数列的末尾(或开头),直到整个数列有序。
const selectsort = (arr) => {
for (let i = 0; i < arr.length; i++) {
let tmpMin = arr[i];
let tmpIndex = i;
for (let j = i; j < arr.length; j++){
if (tmpMin > arr[j]) {
tmpMin = arr[j];
tmpIndex = j;
}
}
[arr[i], arr[tmpIndex]] = [arr[tmpIndex], arr[i]]
}
return arr;
}
console.log(selectsort([4, 2, 6, 1, 5, 8, 7, 3]));
时间复杂度:
插入排序是一种简单直观的排序算法,其基本思想是将待排序序列分为两部分,一部分已经排好序,另一部分待排序,然后将待排序部分的元素依次插入到已排序部分的适当位置,使得插入后仍然有序。
const insertsort = (arr) => {
for (let i = 1; i < arr.length; i++){
let cur = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > cur) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = cur;
}
return arr;
}
console.log(insertsort([4, 2, 6, 1, 5, 8, 7, 3]));
时间复杂度:
归并排序是一种经典的分治算法,它的基本思想是将待排序的数组划分为两个子数组,分别对这两个子数组进行排序,然后将排序后的子数组合并成一个有序数组。具体来说,归并排序的步骤如下:
简易写法:
const quickSort = (arr: number[]): number[] => {
if (arr.length <= 1) return arr;
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
const left: number[] = [];
const right: number[] = [];
for (let i = 0; i < arr.length; i++) {
if (i == pivotIndex) continue;
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
};
console.log(quickSort([4, 2, 6, 1, 5, 8, 7, 3]));
归并排序的时间复杂度是O(nlogn),其中n是待排序数组的长度。虽然归并排序的时间复杂度比较稳定,但需要额外的空间来存储辅助数组,因此其空间复杂度是O(n)。
时间复杂度:假设数组长度为 n,需要进行 logn 次归并操作;每次归并操作需要 O(n) 的时间复杂度;
const quickSort = (arr: number[]): number[] => {
if (arr.length <= 1) return arr;
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
const left: number[] = [];
const right: number[] = [];
for (let i = 0; i < arr.length; i++) {
if (i == pivotIndex) continue;
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
};
希尔排序是一种插入排序的变体,也称为缩小增量排序。其基本思想是将待排序的数组分成若干个子序列,对每个子序列进行插入排序,然后逐步缩小增量,直到增量为1。
具体来说,希尔排序的步骤如下:
const shellSort = (arr: number[]): number[] => {
let gap = Math.floor(arr.length / 2);
while (gap > 0) {
for (let i = gap; i < arr.length; i++) {
const cur = arr[i];
let j = i;
while (j >= gap && arr[j - gap] > cur) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = cur;
}
gap = Math.floor(gap / 2);
}
return arr;
};
console.log(shellSort([4, 2, 6, 1, 5, 8, 7, 3]));
堆排序的实现过程主要分为两个步骤:
堆排序相较于其他排序算法的优势在于,它是一种原地排序算法,只需要一个额外的空间来存放根节点的值,没有浪费的空间。同时,堆排序还具有稳定的时间复杂度和稳定的排序结果。不过,需要注意的是,堆排序对于小规模的数据排序并不是特别高效,适用于大规模数据的排序。
const heapSort = (arr: number[]): number[] => {
const adjustHeap = (arr: number[], i: number, len: number) => {
const tmp = arr[i];
for (let j = 2 * i + 1; j < len; j = 2 * j + 1) {
if (j + 1 < len && arr[j + 1] > arr[j]) j++;
if (arr[j] > tmp) {
arr[i] = arr[j];
i = j;
} else {
break;
}
}
arr[i] = tmp;
};
// 先建立大根堆(从最后一个非叶子节点向上调整)
for (let i = Math.floor(arr.length / 2) - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
// 每次把堆顶的数与最后一个数交换,并重新调整堆
for (let i = arr.length - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
adjustHeap(arr, 0, i);
}
return arr;
};
console.log(heapSort([4, 2, 6, 1, 5, 8, 7, 3]));
const countSort = (arr: number[]): number[] => {
if (arr.length <= 1) return arr;
// 找出最大值和最小值
let max = arr[0];
let min = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
// 统计数列中每个值出现的次数
const count = new Array(max - min + 1).fill(0);
for (let i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
let index = 0;
for (let i = 0; i < count.length; i++) {
while (count[i] > 0) {
arr[index++] = i + min;
count[i]--;
}
}
return arr;
};
console.log(countSort([4, 2, 6, 1, 5, 8, 7, 3]));
const bucketSort = (arr: number[], bucketSize: number): number[] => {
if (arr.length <= 1) return arr;
// 插入排序
function insertionSort(arr: number[]) {
let i;
let j;
let temp;
for (i = 1; i < arr.length; i++) {
temp = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
return arr;
}
// 找到数组中的最大值和最小值
let minValue = arr[0];
let maxValue = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < minValue) minValue = arr[i];
else if (arr[i] > maxValue) maxValue = arr[i];
}
// 计算桶的数量
const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
const buckets = new Array(bucketCount);
for (let i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
// 将数组中的元素分配到各个桶中
for (let i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
// 对每个桶中的元素进行排序
arr.length = 0;
for (let i = 0; i < buckets.length; i++) {
if (buckets[i].length > 0) {
insertionSort(buckets[i]);
for (let j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
}
return arr;
};
console.log(bucketSort([4, 2, 6, 1, 5, 8, 7, 3], 5));
const radixSort = (arr: number[]) => {
const maxDigit = getMaxDigit(arr);
// 获取最大位数
function getMaxDigit(arr: number[]) {
let max = 0;
for (let i = 0; i < arr.length; i++) {
const digit = getDigitCount(arr[i]);
max = Math.max(max, digit);
}
return max;
} // 获取数字的位数
function getDigitCount(num: number) {
if (num === 0) return 1;
return Math.floor(Math.log10(Math.abs(num))) + 1;
} // 获取数字的第i位数字
function getDigit(num: number, i: number) {
return Math.floor(Math.abs(num) / Math.pow(10, i)) % 10;
}
for (let i = 0; i < maxDigit; i++) {
const buckets: number[][] = []; // 创建桶
for (let j = 0; j < arr.length; j++) {
const digit = getDigit(arr[j], i); // 获取数字的第i位数字
if (!buckets[digit]) {
buckets[digit] = [];
}
buckets[digit].push(arr[j]); // 将数字放入相应的桶中
}
const tmp: number[] = [];
arr = tmp.concat(...buckets); // 将桶中的数字取出来,重新放入arr数组中
}
return arr;
};
console.log(radixSort([4, 2, 6, 1, 5, 8, 7, 3]));