携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
一、前言
计数排序 :是一种稳定的排序算法。它不是基于比较的排序算法,因此可以突破 O(n*log(n))
的下界,在线性时间内完成排序。
- 适用于:序列的键值是较小范围的整数,或是可以映射到较小范围整数的情况。
它的算法思想是:
- 统计相同键值的元素个数,然后以键值为下标,把统计结果存储到一个较小的数组中。
- 根据元素的出现个数,再计算出每个元素在排序数组中所在下标。
- 根据这些下标,把元素放到正确的位置上即可。
实现如下:
public class CountingSortFixedK {
private int k;
// 对于数组中元素 x,有 0 <= x <= k, k 是一个较小的数字。
public CountingSortFixedK(int k) {
this.k = k;
}
// Time: O(n+k), Space: O(n+k)
public void sortLeft2Right(int[] arr) {
if (arr == null || arr.length == 0) return;
// 1. 初始化索引数组:统计数字出现次数
int[] indexes = new int[k + 1];
for (int num : arr) ++indexes[num];
// 2. 更新索引数组: 每个元素的开始下标
int start = 0;
for (int i = 0; i <= k; ++i) {
int count = indexes[i];
indexes[i] = start;
start += count;
}
// 3. 遍历原数组,填充排序
int[] tmp = new int[arr.length];
for (int num : arr) {
int idx = indexes[num];
tmp[idx] = num;
++indexes[num];
}
// 拷贝回原数组
System.arraycopy(tmp, 0, arr, 0, arr.length);
}
}
二、题目
(1)排序数组(中)
题干分析
给你一个整数数组 nums
,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
思路解法
计数排数,步骤如下:
- 找最大值和最小值: 因为数字中有负数,目的将负数对应的索引 “取正”
- 定义计数数组并统计
- 遍历输出结果
// 计数排序
// Time: O(n + k), Space: O(n + k), Faster: 99.97%
public int[] sortArrayCounting(int[] nums) {
// 1. 找最大值和最小值
int max = nums[0], min = nums[0];
for (int num : nums) {
max = Math.max(max, num);
min = Math.min(min, num);
}
// 2. 定义计数数组并统计
int[] counts = new int[max - min + 1];
for (int num : nums) {
++counts[num - min]; // 落到数组上
}
// 3. 遍历输出结果
int[] result = new int[nums.length];
for (int i = 0, j = 0; i < max - min + 1; ++i) {
while (counts[i]-- > 0) {
result[j++] = i + min;
}
}
return result;
}