有趣的排序思想

88 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、计数排序 统计每个数字出现的次数

1213322
123
2个3个2个
1122233

应用场景:简单的单值排序问题,排序问题中数据的值域很有限

leetCode1122.数组的相对排序便是用到计数排序的思想

var relativeSortArray = function(arr1, arr2) {
    let cnt = new Array(1001).fill(0);
    for (let x of arr1) cnt[x] += 1; // 计数
    let k = 0;
    for(let x of arr2) while(cnt[x]--) arr1[k++] = x; // 按照arr2顺序排列
    for(let i = 0; i < 1001; i++) {
        if(cnt[i] <= 0) continue;
        while(cnt[i]--) arr1[k++] = i;// 未出现的元素放在arr1的末尾
    }
    return arr1
};

二、基数排序

13211132312221

对个位计数,求得区域尾坐标

个位数123
计数4个2个1个
前缀和467

前4个值存的是个位数为1的值,以此类推;原数组从后往前扫描得到第一次排序

数据特性:保持数据稳定性,两个相同的数据在排序后,他们的相对位置不变

1234567
21113121322213

对十位计数,求得区域尾坐标

十位数123
计数2个3个2个
前缀和257

从后往前扫描得到第二次排序

1234567
11132121223132

leetCode164.最大间距便是使用基数排序,将32位整数分为低16位和高16位,其实我们可以把低16位当作个位,高16位当作十位,便非常好理解

var maximumGap = function(nums) {
    let cnt = new Array(65536).fill(0);
    let temp = new Array(nums.length);
    // 统计低16位次数 涉及到位运算
    for(let x of nums) cnt[x & 0xffff] += 1;
    // 求前缀和
    for(let i = 1; i < 65536; i++) cnt[i] += cnt[i - 1];
    // 从后向前归位
    for(let i = nums.length - 1; i >= 0; i--) temp[--cnt[nums[i] & 0xffff]] = nums[i]
    cnt = new Array(65536).fill(0);// 重置cnt
     // 统计高16位次数
    for(let x of temp) cnt[(x & 0xffff0000) >> 16] += 1;
    // 求前缀和
    for(let i = 1; i < 65536; i++) cnt[i] += cnt[i - 1];
    // 从后向前归位
    for(let i = temp.length - 1; i >= 0; i--) nums[--cnt[(temp[i] & 0xffff0000) >> 16]] = temp[i]
    // 计算最大差值
    let ans = 0;
    for(let i = 1; i < nums.length; i++) {
        ans = Math.max(ans, (nums[i] - nums[i - 1]));
    }
    return ans
};