下面主要是总结下我遇到的一些排序算法,其实我们都知道js中有sort方法,这篇文章只是针对那些对算法有兴趣的人准备的。 为了方便实现,我们先定义几个常规函数
const Compare = {
LESS_THAN: -1,
BIGGER_THAN: 1,
EQUAL: 0,
}
function defaultCompare(a, b) {
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
function swap(array, a, b) {
const temp = array[a];
array[a] = array[b];
array[b] = temp;
}
对于swap也可以使用ES6来实现
function swap(array, a, b) {
[array[a], array[b]] = [array[b], array[a]];
}
冒泡排序
const test_arr0 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28] // 用来测试的数组
function bubbleSort(array, compareFn = defaultCompare) {
const { length } = array;
for (let i = 0; i < length; i++) {
for (let j = 0; j < length - 1 - i; j++) {
if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) {
swap(array, j, j + 1);
}
}
}
return array;
}
const bubble_res = bubbleSort(test_arr0);
console.log('冒泡排序:', bubble_res);
选择排序
const test_arr1 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28] // 用来测试的数组
function selectionSort(array, compareFn = defaultCompare) {
const { length } = array;
let indexMin; // 定义最小的哪个值的index
for (let i = 0; i < length - 1; i++) { // 因为我们要对比所以我们只用轮询次数少一
indexMin = i; // 首先定义的是最小的是当前的i
// 下面的内容就是找到最小的值
for (let j = i; j < length; j++) {
if (compareFn(array[indexMin], array[j]) === Compare.BIGGER_THAN) {
indexMin = j; // 如果当前最小的indexMin不是最小的,那么我们就设置最小的indexMin 是j
}
}
// 如果当前的值不是最小的,我们就交换位置
if (i !== indexMin) {
swap(array, i, indexMin);
}
}
return array;
}
const selection_res = selectionSort(test_arr1);
console.log('选择排序:', selection_res);
插入排序
const test_arr2 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28] // 用来测试的数组
function insertionSort(array, compareFn = defaultCompare) {
const { length } = array;
let temp; // 存储要插入的元素
// 从第二个开始
for (let i = 1; i < length; i++) {
let j = i; // 我们存储i
temp = array[i]; // 当前的值
// 边界条件是j>0以及我们当前的值比它前边的值小
while (j > 0 && compareFn(array[j - 1], temp) === Compare.BIGGER_THAN) {
array[j] = array[j - 1]; // 将前边的值向右移动,移动到当前的值。也就是说当前的值等于前边的值
j--; // 位置变为当前位置的前一个位置
}
array[j] = temp; // 插入到当前的位置
}
return array;
}
const insertion_res = insertionSort(test_arr2);
console.log('插入排序:', insertion_res);
归并排序
const test_arr3 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]; // 用来测试的数组
function mergeSort(array, compareFn = defaultCompare) {
if (array.length > 1) { // 如果数组的长度大于1
const { length } = array;
const middle = Math.floor(length / 2); // 我们这个向下取整
const left = mergeSort(array.slice(0, middle), compareFn);
const right = mergeSort(array.slice(middle, length), compareFn);
array = merge(left, right, compareFn);
}
return array;
}
function merge(left, right, compareFn) {
let i = 0;
let j = 0;
const result = [];
while (i < left.length && j < right.length) {
result.push(
compareFn(left[i], right[j]) === Compare.LESS_THAN ? left[i++] : right[j++]
)
}
return result.concat(i < left.length ? left.slice(i) : right.slice(j));
}
const merge_res = mergeSort(test_arr3);
console.log('归并排序:', merge_res);
快速排序
快速排序的最坏运行情况是O(n²),比如说顺序数列的快排。但它的平摊期望时间是O(n log n) ,且O(n log n)记号中隐含的常数因子很小, 比复杂度稳定等于O(n log n)的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
const test_arr4 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28] // 用来测试的数组
function quickSort(array, left, right, compareFn = defaultCompare) {
var { length } = array; // 获取数组长度
var partitionIndex; // 获取当前操作的index
var left = typeof left != "number" ? 0 : left; // 最左边
var right = typeof right != "number" ? length - 1 : right; // 最右边
// 最左边和最右边对比
if (compareFn(left, right) === Compare.LESS_THAN) {
partitionIndex = partition(array, left, right);
quickSort(array, left, partitionIndex - 1); // 比较基准值左边部分
quickSort(array, partitionIndex + 1, right); // 比较基准值右边的部分
}
return array;
}
function partition(array, left, right, compareFn = defaultCompare) { // 分区操作
var pivot = left; // 设置最左边的值是轴心点(基准值)
var index = left + 1; // 从右边起第一个值开始比较
// 开始循环遍历,从右边起第一个值开始,一直到最右边
for (var i = index; i <= right; i++) {
// 如果当前的值比起基准值小,那么我们就将这些值都放到基准值右边
if (compareFn(array[i], array[pivot]) === Compare.LESS_THAN) {
swap(array, i, index); // 起始位置和当前的i交换
index++; //
}
}
// 此刻,因为基准值右边的值都比他小,所以我们将基准值和当前比他小的值的序列最右边的值交换,此刻,也就是说基准值左边的值都是比他小的
swap(array, pivot, index - 1);
return index - 1; // 返回的值是比当前基准值小的数组的最右边的项
}
const quick_res = quickSort(test_arr4);
console.log('快速排序:', quick_res);
希尔排序
function shellSort(array, compareFn = defaultCompare) {
let { length } = array;
let temp;
let gap = 1;
while (gap < length / 3) { // 动态定义间隔序列
gap = gap * 3 + 1;
}
for (gap; gap > 0; gap = Math.floor(gap / 3)) {
for (var i = gap; i < length; i++) {
temp = array[i];
for (var j = i - gap; j >= 0 && array[j] > temp; j -= gap) {
array[j + gap] = array[j];
}
array[j + gap] = temp;
}
}
return array;
}
const test_arr5 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28] // 用来测试的数组
const shell_res = shellSort(test_arr5);
console.log('希尔排序:', shell_res);
计数排序(注意这个只是适合整数排序)
function countingSort(array) {
if (array.length < 2) {
return array;
}
const maxValue = findMaxValue(array);
const counts = new Array(maxValue + 1); // 这里创建了一个长度为当前数组中最大值的长度的数组
array.forEach(element => {
// console.log(typeof counts); // object
if (!counts[element]) {
counts[element] = 0;
}
counts[element]++;
});
// console.log('第一步遍历之后的counts', counts); // 结果就是得到了 counts[1:1,2:1,28:1....];其实是一个对象
let sortedIndex = 0;
counts.forEach((count, i) => {
// console.log(count, i); // 1 1 1 2 1 28 1 36 2 45 ...
while (count > 0) {
array[sortedIndex++] = i;
count--;
}
})
return array;
}
const test_arr6 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28];
const counting_res = countingSort(test_arr6);
console.log('计数排序:', counting_res);
function findMaxValue(array) {
let max = array[0];
for (let i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}