计数排序算法优化,增加对负数及小数的支持

计数排序算法优化

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;
}

请注意,此代码只能用于整数数组,并且需要额外的空间来存储计数数组。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值