本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、计数排序 统计每个数字出现的次数
| 1 | 2 | 1 | 3 | 3 | 2 | 2 |
|---|
| 1 | 2 | 3 |
|---|---|---|
| 2个 | 3个 | 2个 |
| 1 | 1 | 2 | 2 | 2 | 3 | 3 |
|---|
应用场景:简单的单值排序问题,排序问题中数据的值域很有限
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
};
二、基数排序
| 13 | 21 | 11 | 32 | 31 | 22 | 21 |
|---|
对个位计数,求得区域尾坐标
| 个位数 | 1 | 2 | 3 |
|---|---|---|---|
| 计数 | 4个 | 2个 | 1个 |
| 前缀和 | 4 | 6 | 7 |
前4个值存的是个位数为1的值,以此类推;原数组从后往前扫描得到第一次排序
数据特性:保持数据稳定性,两个相同的数据在排序后,他们的相对位置不变
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|
| 21 | 11 | 31 | 21 | 32 | 22 | 13 |
对十位计数,求得区域尾坐标
| 十位数 | 1 | 2 | 3 |
|---|---|---|---|
| 计数 | 2个 | 3个 | 2个 |
| 前缀和 | 2 | 5 | 7 |
从后往前扫描得到第二次排序
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|
| 11 | 13 | 21 | 21 | 22 | 31 | 32 |
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
};