计数排序
基本思想
? 计数排序对一定量的整数排序的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序。
对于一个输入数组中的一个元素i,只要我们知道了这个数组中比i小的元素的个数x,那么我们就可以直接把i放到第(x+1)个位置,即i的索引为x(索引从0开始)。
算法步骤
?1. 找出数组中的最大值和最小值;
2. 统计数组中每个值i的元素出现的次数,将其存入新数组的第i项;
3. 从最小值开始依次计算小于该元素的元素个数x,则该元素在有序数组中的索引值为x
或者:
- 找出数组中的最大值和最小值;
- 统计数组中每个值i的元素出现的次数,将其存入新数组的第i项;
- 将上面的数组根据索引i的值依次在新数组中展开索引i,其结果则为有序数组。
复杂度和稳定性
时间复杂度:O(n+k);n为数组长度,k为最大最小值差
空间复杂度:O(k)
稳定性:稳定(和实现有关)
代码
//不稳定
static void countingSort(int[] nums) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i : nums) {
if (i < min) {
min = i;
}
if (i > max) {
max = i;
}
}
int[] counter = new int[max - min + 1];
for (int i : nums) {
counter[i - min]++;
}
int p = 0;
for (int i = 0; i < counter.length; i++){
for (int j = 0; j < counter[i]; j++){
nums[p++] = i + min;
}
}
}
//稳定
static void countingSort2(int[] nums) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i : nums) {
if (i < min) {
min = i;
}
if (i > max) {
max = i;
}
}
int[] counter = new int[max - min + 1];
for (int i : nums) {
counter[i - min]++;
}
for (int i = 1; i < counter.length; i++)
counter[i] += counter[i - 1];
int[] output = new int[nums.length];
for (int i = nums.length - 1; i >= 0; i--){
int index = counter[nums[i] - min] - 1;
if (index < 0)continue;
output[index] = nums[i];
counter[nums[i] - min]--;
}
System.arraycopy(output, 0, nums, 0, nums.length);
}
桶排序
基本思想
桶排序是计数排序的升级版。它利用了函数的映射关系(比较规则),高效与否的关键就在于这个映射函数的确定(计数排序可以看做函数映射关系为f(x)=x或f(x)=x-min)。
假设输入由一个随机过程产生,该过程将元素一致地分布在区间[0,1)上。桶排序的思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。
算法步骤
? 1) 建立一个函数的映射关系f(x)=ax+b等;
2) 根据映射关系建立多个桶f(x);
3) 依次将数据放入对应桶中;
4) 对桶内数据进行排序,可以继续使用桶排序,也可以使用其他排序;
5) 依次输出各个桶内数据,即为有序序列。
复杂度和稳定性
最佳情况:T(n) = O(n+k);
最差情况:T(n) = O(n2);
平均情况:T(n) = O(n+k);
稳定性:稳定(但若桶内排序不稳定则整体不稳定);
空间复杂度:O(n+k)。
代码
static void bucketSort(int[] nums) {
//映射规则为:f(x)= x/10-c,其中常量位:c=min/10
//排序算法为直接插入排序
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i : nums) {
if (i < min) {
min = i;
}
if (i > max) {
max = i;
}
}
List<List<Integer>> buckets = new ArrayList<>();
for (int i = 0; i < max / 10 - min / 10 + 1; i++) {
buckets.add(new ArrayList<Integer>());
}
for (int i : nums) {
int index = i / 10 - min / 10;
buckets.get(index).add(i);
}
int p = 0;
for (List<Integer> i : buckets) {
if (i.isEmpty()) continue;
int[] arr = i.stream().mapToInt(Integer::valueOf).toArray();
insertSort(arr);
for (int j : arr){
nums[p++] = j;
}
}
}
基数排序
基本思想
基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。也可以从最高为开始排。
但是需要注意的是对不同位进行排序的时候必须使用稳定的排序。
代码
排序条件:全为整数,且没有负数
static void radixSort(int[] nums){
int max = Arrays.stream(nums).max().getAsInt();
for (int exp = 1; max / exp > 0; exp *= 10){
expSort(nums, exp);
}
}
static void expSort(int[] nums, int exp){
int[] buckets = new int[10];
for (int i : nums) {
buckets[(i / exp) % 10]++;
}
for (int i = 1; i < buckets.length; i++)
buckets[i] += buckets[i - 1];
int[] output = new int[nums.length];
for (int i = nums.length - 1; i >= 0; i--){
int index = buckets[(nums[i] / exp) % 10] - 1;
if (index < 0)continue;
output[index] = nums[i];
buckets[(nums[i] / exp) % 10]--;
}
System.arraycopy(output, 0, nums, 0, nums.length);
}
时间复杂度:O(n * k),n为输入数组长度,k为输入数组位数
空间复杂度:O(n)
稳定性:稳定
原文地址:https://www.cnblogs.com/hao-blogs/p/12635349.html