最近哈士奇在刷题的过程中看到了很多排序算法的题目,那么究竟有哪些排序算法呢?哈士奇在翻阅资料的过程中发现了十种算法,并且将其总结了出来,下面和大家分享哈士奇学到的排序算法。
1. 冒泡排序(Bubble Sort)
冒泡排序是一种简单直观的排序算法。它重复地遍历待排序数组,比较相邻的两个元素,并依次将较大(或较小)的元素交换到右侧,直到整个数组排序完成。
优点:
- 实现简单,容易理解和实现。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 效率较低,时间复杂度为O(n^2),不适用于大规模数据。
- 不适合已经基本有序的数据集。
示例:
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(bubbleSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于简单排序需求且数据量较小的情况。
2. 选择排序(Selection Sort)
选择排序是一种简单直观的排序算法,它每次从未排序部分选择最小(或最大)的元素,与未排序部分的第一个元素交换位置,直到整个数组排序完成。
优点:
- 实现简单,容易理解和实现。
- 不占用额外的内存空间。
缺点:
- 效率较低,时间复杂度为O(n^2)。
- 不适合大规模数据集。
示例:
function selectionSort(arr) {
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;
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
return arr;
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(selectionSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于简单排序需求且数据量较小的情况。
3. 插入排序(Insertion Sort)
插入排序是一种简单直观的排序算法,它通过构建有序序列,对于未排序的数据逐个插入到已排序序列的合适位置。
优点:
- 对于小规模数据或基本有序的数据效果较好。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 效率较低,时间复杂度为O(n^2)。
- 不适合大规模数据集。
示例:
function insertionSort(arr) {
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];
j--;
}
arr[j + 1] = key;
}
return arr;
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(insertionSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于小规模数据或基本有序的情况。
4. 希尔排序(Shell Sort)
希尔排序是插入排序的一种改进版本,它通过将整个序列分割成若干个子序列进行插入排序,最后再对整个序列进行一次插入排序。
优点:
- 相对于插入排序,希尔排序在大规模数据时有较好的性能。
- 不稳定排序算法,可能改变相等元素的相对位置。
缺点:
- 实现稍复杂,不如其他简单排序算法易于理解。
示例:
function shellSort(arr) {
const len = arr.length;
let gap = Math.floor(len / 2);
while (gap > 0) {
for (let i = gap; i < len; i++) {
let temp = arr[i];
let j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap = Math.floor(gap / 2);
}
return arr;
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(shellSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于大规模数据的排序需求。
5.归并排序(Merge Sort)
归并排序采用分治法,将问题分解成较小的子问题,然后递归地解决子问题,最后将解决方案合并起来得到原问题的解决方案。
优点:
- 时间复杂度为O(nlogn),效率较高。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 需要额外的内存空间来存储中间结果。
- 实现稍复杂,不如其他简单排序算法易于理解。
示例:
function mergeSort(arr) {
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, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(mergeSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于任意大小的数据集,尤其在需要稳定排序算法的情况下。
6.堆排序(Heap Sort)
堆排序利用堆这种数据结构进行排序。它将待排序的序列构造成一个大顶堆(或小顶堆),然后依次取出堆顶元素,再调整堆,直到所有元素排序完成。
优点:
- 时间复杂度为O(nlogn),效率较高。
- 不需要额外的存储空间。
缺点:
- 不稳定排序算法,可能改变相等元素的相对位置。
- 实现较复杂,需要理解和实现堆的数据结构。
示例:
function heapSort(arr) {
buildMaxHeap(arr);
for (let i = arr.length - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
maxHeapify(arr, 0, i);
}
return arr;
}
function buildMaxHeap(arr) {
for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
maxHeapify(arr, i, arr.length);
}
}
function maxHeapify(arr, i, length) {
let left = 2 * i + 1;
let right = 2 * i + 2;
let largest = i;
if (left < length && arr[left] > arr[largest]) {
largest = left;
}
if (right < length && arr[right] > arr[largest]) {
largest = right;
}
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
maxHeapify(arr, largest, length);
}
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(heapSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于需要稳定排序算法且数据量较大的情况。
7.快速排序(Quick Sort)
快速排序采用分治法,选择一个基准元素,将小于基准的元素放在左侧,大于基准的元素放在右侧,然后递归地对左右两侧的子数组进行排序。
优点:
- 时间复杂度为O(nlogn),效率较高。
- 原地排序算法,不需要额外的存储空间。
缺点:
- 不稳定排序算法,可能改变相等元素的相对位置。
- 对于已经基本有序的数组,效率较低。
示例:
function quickSort(arr, left = 0, right = arr.length - 1) {
if (left < right) {
const pivotIndex = partition(arr, left, right);
quickSort(arr, left, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, right);
}
return arr;
}
function partition(arr, left, right) {
const pivot = arr[right];
let i = left;
for (let j = left; j < right; j++) {
if (arr[j] < pivot) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
[arr[i], arr[right]] = [arr[right], arr[i]];
return i;
}
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(quickSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
应用场景:适用于任意大小的数据集,尤其在需要原地排序算法的情况下。
8.计数排序(Counting Sort)
计数排序是一种非比较排序算法,它通过确定每个元素在输入序列中的位置来排序。它适用于元素范围较小且已知的情况。
优点:
- 时间复杂度为O(n+k),其中n是元素的数量,k是元素的范围,效率较高。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 需要额外的存储空间来存储计数数组。
- 仅适用于元素范围较小且已知的情况。
示例:
function countingSort(arr) {
const max = Math.max(...arr);
const min = Math.min(...arr);
const count = Array(max - min + 1).fill(0);
const output = [];
arr.forEach(num => count[num - min]++);
count.forEach((num, index) => {
for (let i = 0; i < num; i++) {
output.push(index + min);
}
});
return output;
}
const arr = [4, 2, 2, 8, 3, 3, 1];
console.log(countingSort(arr)); // [1, 2, 2, 3, 3, 4, 8]
应用场景:适用于元素范围较小且已知的情况,例如对字符进行排序。
9.桶排序(Bucket Sort)
桶排序将元素分配到有限数量的桶中,对每个桶中的元素进行排序,最后将所有桶中的元素按照顺序合并。
优点:
- 对于均匀分布的数据,效率较高。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 需要额外的存储空间来存储桶。
- 对于数据分布不均匀的情况,可能效率较低。
示例:
function bucketSort(arr, bucketSize = 5) {
if (arr.length === 0) {
return arr;
}
const min = Math.min(...arr);
const max = Math.max(...arr);
const bucketCount = Math.floor((max - min) / bucketSize) + 1;
const buckets = Array.from({ length: bucketCount }, () => []);
arr.forEach(num => {
const bucketIndex = Math.floor((num - min) / bucketSize);
buckets[bucketIndex].push(num);
});
return buckets.reduce((acc, bucket) => acc.concat(bucket.sort((a, b) => a - b)), []);
}
const arr = [29, 25, 3, 49, 9, 37, 21, 43];
console.log(bucketSort(arr)); // [3, 9, 21, 25, 29, 37, 43, 49]
应用场景:适用于均匀分布的数据,例如对学生成绩进行排序。
10.基数排序(Radix Sort)
基数排序是一种多关键字的排序算法,它根据每个关键字的值来排序。它从最低有效位(或者最高有效位)开始,依次对每个关键字进行排序。
优点:
- 对于整数或字符串等具有多关键字的数据,效率较高。
- 稳定排序算法,不会改变相等元素的相对位置。
缺点:
- 对于关键字位数较大的数据,可能需要较大的存储空间。
- 实现相对复杂,不如其他简单排序算法易于理解。
示例:
function radixSort(arr) {
const max = Math.max(...arr);
let exp = 1;
while (Math.floor(max / exp) > 0) {
arr = countingSortForRadix(arr, exp);
exp *= 10;
}
return arr;
}
function countingSortForRadix(arr, exp) {
const output = [];
const count = Array(10).fill(0);
arr.forEach(num => {
const digit = Math.floor(num / exp) % 10;
count[digit]++;
});
for (let i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
for (let i = arr.length - 1; i >= 0; i--) {
const digit = Math.floor(arr[i] / exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
return output;
}
const arr = [329, 457, 657, 839, 436, 720, 355];
console.log(radixSort(arr)); // [329, 355, 436, 457, 657, 720, 839]
应用场景:适用于需要多关键字排序的情况,例如对字符串进行排序。
以上就是哈士奇介绍的所有排序算法,假如您也和我一样,在准备春招。欢迎加我微信shunwuyu ,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!