算法 - 计数排序(Counting Sort)

  • 之前学习的冒泡、选择、插入、归并、快速、希尔、堆排序,都是基于比较的排序
  • 平均时间复杂度目前最低是O(nlogn)
  • 计数排序、桶排序、基数排序,都不是基于比较的排序
  • 它们是典型的用空间换时间,在某些时候,平均时间复杂度可以比O(nlogn)更低
  • 计数排序于1954年由Harold H.Seward提出,适合对一定范围内的整数进行排序
  • 计数排序的核心思想
  • 统计每个整数在序列中出现的次数,进而推导出每一个整数在有序序列中的索引

最简单的实现

在这里插入图片描述

int max = array[0]; // 最大值
for (int i = 1; i< array.length; i++) {
    if (array[i] > max) {
        max = array[i];
    }
}
// 统计元素出现的次数
int[] counts = new int[max + 1];
for (int i = 0; i < array.length; i++) {
    counts[array[i]]++;
}
// 按顺序赋值
int index = 0;
for (int i = 0; i < counts.length; i++) {
    while (counts[i]-- > 0) {
        array[index++] = i;
    }
}
  • 这个版本的实现存在以下问题:
  1. 无法对负整数进行排序
  2. 极其浪费内存空间
  3. 是个不稳定的排序

改进思路

在这里插入图片描述

  • 假设array中的最小值是min
  • array中的元素k对应的counts索引是k-min
  • array中的元素k在有序序列中的索引
  • counts[k - min] - p
  • p代表着是倒数第几个k
    比如:
  • 元素8在有序序列中的索引
  • counts[8 - 3] - 1,结果是7
  • 倒数第1个元素7在有序序列中的索引
  • counts[7 - 3] - 1,结果是6
  • 倒数第2个元素7在有序序列中的索引
  • counts[7 - 3] - 2,结果是5
    在这里插入图片描述
    在这里插入图片描述

改进实现

int max = array[0]; // 最大值
int min = array[0]; // 最小值
for (int i = 1; i < array.length; i++) {
    if (array[i] > max) [
        max = array[i];
    ]
    if (array[i] < min) {
        min = array[i];
    }
}
// 用于计数
int[] counts = new int[max - min + 1];
for (int i = 0; i < array.length; i++) {
    counts[array[i] - min]++;
}
for (int i = 1; i < counts.length; i++) {
    counts[i] += counts[i - 1];
}
// 用于存放排好序的数据
int[] output = new int[array.length];
for (int i = array.length - 1; i >= 0; i--) {
    output[--counts[array[i] - min]] = array[i];
}
for (int i = 0; i < array.length; i++) {
    array[i] = output[i];
}
  • 最好、最坏、平均时间复杂度:O(n + k)
  • 空间复杂度:O(n + k)
  • k是整数的取值范围
  • 属于稳定排序

对自定义对象进行排序

  • 如果自定义对象可以提供用以排序的整数类型,依然可以使用计数排序
private static class Person {
    int age;
    Stirng name;
    Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
    public String toString() {
        return "Person [age=" + age + ", name ="" + name + "]";
    }
}
Person[] persons = new Person[] {
    new Person(20, "A");
    new Person(-13, "B");
    new Person(17, "C");
    new Person(12, "D");
    new Person(-13, "E");
    new Person(20, "F");
}
// 用于计数
int[] counts = new int[max - min + 1];
for (int i = 0; i < persons.length; i++) {
    counts[persons.[i].age - min]++;
}
for (int i = 1; i < counts.length; i++) {
    counts[i] += counts[i - 1];
}
// 用于存放排好序的数据
Person[] output = new Persons[persons.length];
for (int i = persons.length - 1; i >= 0; i--) {
    output[--counts[persons[i].age - min]] = persons[i];
}
  • 排序之后的结果:
Person[age = -13, name = B]
Person[age = -13, name = E]
Person[age = 12, name = D]
Person[age = 17, name = C]
Person[age = 20, name = A]
Person[age = 20, name = F]

最终版本

public int[] sortArray(int[] nums) {
	int max = nums[0]; // 最大值
	int min = nums[0]; // 最小值
	for (int i = 1; i < nums.length; i++) {
	    if (nums[i] > max)  max = nums[i];
	    if (nums[i] < min)  min = nums[i];
	}
	// 用于计数
	int[] counts = new int[max - min + 1];
	for (int i = 0; i < nums.length; i++) {
	    counts[nums[i] - min]++;
	}
	for (int i = 1; i < counts.length; i++) {
	    counts[i] += counts[i - 1];
	}
	// 用于存放排好序的数据
	int[] output = new int[array.length];
	for (int i = nums.length - 1; i >= 0; i--) {  // 为了稳定性
   		output[--counts[nums[i] - min]] = nums[i];
	}
	return output;
}
发布了163 篇原创文章 · 获赞 18 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/songzhuo1991/article/details/102797461
今日推荐