计数排序算法优化
1.增加对负数及小数的支持
2.当数列最大最小值差距过大时,其空间复杂度超过NlogN时,采用时间复杂度小于等于NlogN的排序算法
优化后的计算排序算法:
经过改进后的计数排序,不在局限于正整数的排序,且当数列中数据的数值差距过大时也可以适用;
时间复杂度最好 O(n) 最差O(NlogN),当数列最大最小值差距不大且整个数列非常紧凑时,时间复杂度为O(n) ,当情况较坏数列较稀疏时,时间复杂度为O(NlogN)。
// 计数排序
const countSort = (arr) => {
// 降维因子,数组所有的待排序值,都降维,这样复杂度O(n+k),里面的空间复杂度K降低100倍;如果0.01K相较于N较小的话,则可以忽略不计,则复杂度为O(n)
const factor = 0.01;
const mfactor = 100;
arr = arr.map(num => mul(num, factor));
// 找出数组中的最大值
const maxValue = Math.floor(Math.max(...arr));
const minValue = Math.floor(Math.min(...arr));
const maxArrLength = Math.floor(maxValue - minValue) + 2;
const substituteSortHandle = mergeSort; // 这里的替代算法选用的并归排序
// 计算计数排序的空间复杂度,如果工具复杂度大于NlogN,则采用其他复杂度小于NlogN的排序算法
const complexity = arr.length * Math.log(arr.length);
if(maxArrLength > complexity) { // 如果计数排序算法空间复杂度大于NlogN时,用其他排序算法如并归
return substituteSortHandle(arr).map(num => mul(num, mfactor)); // 当计数排序在当数列最大最小值差距过大时,如超过NlogN,用其他排序算法
}
// 初始化计数数组并将其全部赋值为 0
const counts = new Array(maxArrLength).fill(0);
const bucketMap = new Map();
// 遍历数组,并将对应的计数数组元素加 1
for (const item of arr) {
const index = Math.floor(item) - minValue;
let array = bucketMap.get(index);
if(array == null || array == '' || typeof array == 'undefined'){
array = [];
}
array.push(item);
array = substituteSortHandle(array); // 对于小数的处理通过并归排序处理,这里也可以选其他的排序算法,因为原始的计数排序不支持小数,所以不能用递归函数处理
bucketMap.set(index, array);
counts[index]++;
}
// 初始化结果数组
let sortedArr = [];
// 遍历计数数组,将元素值大于 0 的数字加入结果数组
for (let i = 0; i < counts.length; i++) {
const maxLength = Math.floor(counts[i]);
while (counts[i] > 0) {
let array = bucketMap.get(i);
sortedArr.push(array[maxLength - counts[i]--]);
}
}
// 返回结果数组
return sortedArr.map(num => mul(num, mfactor));
}
// 乘法运算
const mul = (arg1, arg2) => {
var r1 = arg1.toString(), // 将传入的数据转化为字符串
r2 = arg2.toString(),
m, resultVal, d = null;
m = (r1.split(".")[1] ? r1.split(".")[1].length : 0) + (r2.split(".")[1] ? r2.split(".")[1].length : 0); // 获取两个数字的小数位数的和
resultVal = Number(r1.replace(".", "")) * Number(r2.replace(".", "")) / Math.pow(10, m); // 乘积的算法就是去掉小数点做整数相乘然后除去10的所有小数位的次方
return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
}
// 合并左右两侧节点子函数
const merge = (left, right) => {
let result = [];
while (left.length > 0 && right.length > 0) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
return result.concat(left).concat(right);
}
// 归并排序
const mergeSort = (array) => {
if (array.length <= 1) {
return array;
}
let middle = Math.floor(array.length / 2);
let left = array.slice(0, middle);
let right = array.slice(middle);
left = mergeSort(left);
right = mergeSort(right);
return merge(left, right);
}
原生的计数排序算法:
计数排序是一种非比较排序算法。它的基本思想是对于给定的输入序列中的每一个元素,确定该元素在输入序列中的出现次数。然后根据出现次数对所有元素进行排序。
下面是一个示例JS代码:
// 原生计算排序算法
function countSort(arr) {
var max = Math.max.apply(null, arr);
var min = Math.min.apply(null, arr);
var count = new Array(max - min + 1).fill(0);
for (var i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
var index = 0;
for (var i = min; i <= max; i++) {
while (count[i - min]-- > 0) {
arr[index++] = i;
}
}
return arr;
}
请注意,此代码只能用于整数数组,并且需要额外的空间来存储计数数组。