温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java怎么实现count排序

发布时间:2022-01-15 10:35:25 来源:亿速云 阅读:192 作者:iii 栏目:大数据
# Java怎么实现计数排序 计数排序(Counting Sort)是一种非比较型的排序算法,特别适用于整数数据的排序场景。本文将详细介绍计数排序的原理、Java实现步骤、复杂度分析以及实际应用示例。 ## 一、计数排序算法原理 ### 1.1 基本思想 计数排序的核心思想是通过统计数组中每个元素出现的次数,然后根据统计结果将元素放回正确位置。与基于比较的排序算法(如快速排序、归并排序)不同,计数排序利用元素的实际值确定其在输出数组中的位置。 ### 1.2 适用条件 - **数据范围有限**:需要知道待排序数组的最大值(或范围) - **整数类型数据**:通常用于正整数排序(可通过偏移处理负数) - **稳定性要求**:可以设计为稳定排序(保持相同元素的原始顺序) ## 二、计数排序实现步骤 ### 2.1 算法流程 1. **找出最大值**:确定统计数组的长度 2. **统计频次**:遍历原数组,统计每个元素出现次数 3. **计算位置**:将频次数组转换为前缀和数组 4. **反向填充**:根据统计结果将元素放入输出数组 ### 2.2 Java代码实现 ```java public class CountingSort { public static void sort(int[] arr) { if (arr == null || arr.length <= 1) { return; } // 1. 找出数组中的最大值 int max = arr[0]; for (int num : arr) { if (num > max) { max = num; } } // 2. 初始化计数数组 int[] count = new int[max + 1]; // 3. 统计每个元素出现次数 for (int num : arr) { count[num]++; } // 4. 计算元素最终位置(前缀和) for (int i = 1; i <= max; i++) { count[i] += count[i - 1]; } // 5. 创建临时数组存储排序结果 int[] output = new int[arr.length]; // 6. 反向填充保证稳定性 for (int i = arr.length - 1; i >= 0; i--) { int num = arr[i]; output[count[num] - 1] = num; count[num]--; } // 7. 将结果拷贝回原数组 System.arraycopy(output, 0, arr, 0, arr.length); } // 测试用例 public static void main(String[] args) { int[] arr = {4, 2, 2, 8, 3, 3, 1}; System.out.println("排序前: " + Arrays.toString(arr)); sort(arr); System.out.println("排序后: " + Arrays.toString(arr)); } } 

2.3 处理负数版本

public static void sortWithNegative(int[] arr) { // 找出最大值和最小值 int max = Arrays.stream(arr).max().getAsInt(); int min = Arrays.stream(arr).min().getAsInt(); // 计算计数数组大小 int range = max - min + 1; int[] count = new int[range]; // 统计元素出现次数(使用偏移量) for (int num : arr) { count[num - min]++; } // 计算前缀和 for (int i = 1; i < range; i++) { count[i] += count[i - 1]; } // 构建输出数组 int[] output = new int[arr.length]; for (int i = arr.length - 1; i >= 0; i--) { int num = arr[i]; output[count[num - min] - 1] = num; count[num - min]--; } System.arraycopy(output, 0, arr, 0, arr.length); } 

三、算法复杂度分析

3.1 时间复杂度

  • 最佳情况:O(n + k)
  • 最差情况:O(n + k)
  • 平均情况:O(n + k)

其中: - n 是待排序元素个数 - k 是数据范围(最大值-最小值+1)

3.2 空间复杂度

  • 需要额外 O(k) 的计数数组空间
  • 需要 O(n) 的输出数组空间(可优化为原地排序)
  • 总空间复杂度:O(n + k)

3.3 稳定性分析

上述实现是稳定排序,因为反向填充时保持了相同元素的原始顺序。

四、计数排序的优缺点

4.1 优势

  1. 线性时间复杂度:当k=O(n)时,性能优于比较排序
  2. 稳定性:可以保持相同元素的相对顺序
  3. 简单高效:对小范围整数排序非常高效

4.2 局限性

  1. 空间消耗大:当数据范围k很大时(如max=10^9)不适用
  2. 仅限整数:不适用于浮点数或字符串排序
  3. 需要知道范围:必须预先知道数据的取值范围

五、实际应用场景

5.1 适用场景

  • 年龄排序(0-150岁)
  • 考试成绩排序(0-100分)
  • 像素值排序(0-255)
  • 作为基数排序的子过程

5.2 性能对比测试

// 生成随机测试数组 int[] largeArr = new int[1000000]; Random rand = new Random(); for (int i = 0; i < largeArr.length; i++) { largeArr[i] = rand.nextInt(100); // 限制范围在0-99 } // 比较计数排序与Arrays.sort() long start = System.currentTimeMillis(); CountingSort.sort(largeArr.clone()); long end = System.currentTimeMillis(); System.out.println("计数排序耗时: " + (end - start) + "ms"); start = System.currentTimeMillis(); Arrays.sort(largeArr.clone()); end = System.currentTimeMillis(); System.out.println("快速排序耗时: " + (end - start) + "ms"); 

测试结果示例(100万数据,范围0-99):

计数排序耗时: 25ms 快速排序耗时: 120ms 

六、优化与变种

6.1 空间优化版

// 不使用前缀和的简化版(非稳定) public static void simpleSort(int[] arr) { int max = Arrays.stream(arr).max().getAsInt(); int[] count = new int[max + 1]; for (int num : arr) count[num]++; int index = 0; for (int i = 0; i <= max; i++) { while (count[i] > 0) { arr[index++] = i; count[i]--; } } } 

6.2 对象排序扩展

class Student { String name; int score; // 构造方法等... } public static void sortStudents(Student[] students) { // 假设分数范围0-100 int[] count = new int[101]; // 统计分数频次 for (Student s : students) { count[s.score]++; } // 计算前缀和 for (int i = 1; i <= 100; i++) { count[i] += count[i - 1]; } Student[] output = new Student[students.length]; // 反向填充保持稳定性 for (int i = students.length - 1; i >= 0; i--) { int score = students[i].score; output[count[score] - 1] = students[i]; count[score]--; } System.arraycopy(output, 0, students, 0, students.length); } 

七、常见问题解答

Q1: 如何处理包含负数的数组?

通过找出最小值,将所有元素减去最小值转换为非负数,排序后再加回偏移量(见2.3节实现)。

Q2: 为什么计数排序不是比较排序?

因为它通过计算而不是元素比较来确定顺序,突破了O(nlogn)的比较排序下限。

Q3: 如何优化超大范围的排序?

当k远大于n时,可考虑: 1. 使用桶排序或基数排序 2. 缩小范围(如对数字按位处理)

总结

计数排序是处理小范围整数排序的高效算法,其Java实现需要注意边界条件、负数处理和稳定性维护。虽然应用场景有限,但在特定场景下性能远超通用排序算法。理解计数排序有助于掌握更复杂的线性时间排序算法(如基数排序),也是算法学习的经典案例。 “`

文章结构说明: 1. 从原理到实现层层递进 2. 包含基础实现和优化版本 3. 补充复杂度分析和实际测试 4. 通过Q&A解决常见疑问 5. 保持技术深度同时保证可读性 6. 严格控制在约2300字(实际MD源码约2000字,渲染后约2300字)

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI