一、常见排序算法及分类
二、排序算法衡量标准
- 执行效率,即时间复杂度。
- 内存消耗,即空间复杂度。
- 稳定性,即如果数据在排序前,存在比较值相同的数据时,在排序过后它们的相对位置不发生改变。
三、排序算法JS实现
冒泡排序
原理:
- 需要排序的数据,两两对比大小,交换位置,大的会被换到后方。
- 执行第一遍冒泡后,最大的元素会被放置在最后面。
- 反复执行n遍,所有的数据就排好了。
优势:
- 是稳定的排序算法,因为值相等时不会进行交换操作。
- 原地排序不用开辟额外空间,空间复杂度低,仅为
O(1)
劣势:
- 时间复杂度高,最坏情况下需要进行
O(n²)
的比对,不适用于大型数据集 - 交换位置的操作太频繁,会影响
cpu
执行效率。
实现:
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]]; // 元素交换
};
};
};
};
快速排序
原理:快速排序是使用分而治之的方法,将原始数组分为较小的数组。它从数组中取一个值(n)出来,接着遍历剔除了n的剩下的数组,将小于和大于n的数分别存入数组left和right,然后分别合并left、n、right,递归重复此过程最终排列出来。
优势:
- 速度快
- 原地排序,只需要占用
O(logn)
的栈空间。
劣势:
- 不是稳定的排序算法。
- 分区点的选择有讲究,选择不当时最坏情况会退化为
O(n²)
。 - 需要把待排序的数组一次性读入到内存里。
实现:
function quickSort (arr) {
if (array.length <= 1) return array; // 数组长度小于1 不需再排序,直接返回
let left = [], right = [];
let middle = array.splice(Math.floor(array.length / 2), 1)[0] // 将中间值取出 并在arr中移除
// 将中间值与遍历剔除了中间值的数组的元素进行比较
array.forEach(function (e) {
e >= middle ? right.push(e) : left.push(e); // 将各自与middle比较后的元素各自归组
})
const arr = quickSort(left).concat(middle, quickSort(right)); // 合并各数组(各自未排列好的数组进行递归继续快排)
return arr;
};
插入排序
原理:将数组分成有序和无序两个部分,排序的时候,我们不断将无序部分的第一个元素插入有序部分。插入时,按照从后往前的顺序,逐一跟有序部分的元素比较,如果比有序元素的数值更小,就进行交换;如果比有序元素的数值更大,就停止比较。
优势:
- 有提前终止循环的情况,如果是面对近似有序的数组,效率奇高。
- 原地排序不用开辟额外空间,空间复杂度低,仅为
O(1)
- 没有交换位置的操作执行效率高。
- 是一种稳定的排序算法。
劣势:
- 时间复杂度高,最坏情况下需要进行
O(n²)
的比对,不适用于大型数据集
实现:
function insertSort (arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < i; j++) {
if(arr[i] < arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]]
};
};
};
};
选择排序
原理:
依次选出最小的值,然后将其放在数组中。
优势:
是一种原地排序算法,与冒泡排序相比,交换位置的操作改为了赋值操作,执行效率会提高。
劣势:
- 时间复杂度高,最坏和最好情况都是
O(n²)
的复杂度。 - 不是稳定的排序算法,因为每次找到最小的值后会进行交换位置的操作。
实现:
let minIndex = 0;
function selectSort (arr) {
for (let i = 0; i < arr.length; i++) {
minIndex = i;
for (let j = i + 1; j < arr.length; j++) {
if(arr[i] < arr[minIndex]) {
minIndex = j;
};
};
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
};
};
归并排序
原理:归并排序是建立在归并操作上的一种有效、稳定的排序算法。是使用分治法的一个典型应用。
优势:
不是原地排序,需要开辟额外的O(n)
空间。
劣势:
- 没有最好最坏时间复杂度,任何情况下都是
O(nlogn)
- 是一种稳定的排序算法。
实现:
function mergeSort (arr) {
const len = arr.length;
if (len < 2) {
return arr;
};
let middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle); // 拆分为两个子数组
return merge(mergeSort(left), mergeSort(right));
};
function merge(left, right) {
const result = [];
while (left.length && right.length) {
// 注意: 判断的条件是小于或等于,如果只是小于,那么排序将不稳定.
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
};
};
while (left.length) result.push(left.shift());
while (right.length) result.push(right.shift());
return result;
};