【java实现+4种变体完整例子】排序算法中【计数排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

news2025/4/20 5:17:35

以下是计数排序的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格:
在这里插入图片描述


一、计数排序基础实现

原理

通过统计每个元素的出现次数,按顺序累加得到每个元素的最终位置,并填充到结果数组中。

代码示例
public class CountingSort {
    void sort(int[] arr) {
        if (arr.length == 0) return;

        // 找到数据范围
        int min = Arrays.stream(arr).min().getAsInt();
        int max = Arrays.stream(arr).max().getAsInt();
        int range = max - min + 1;

        // 统计每个元素出现的次数
        int[] count = new int[range];
        for (int num : arr) {
            count[num - min]++;
        }

        // 累加统计结果,得到每个元素的最终位置
        for (int i = 1; i < count.length; 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);
    }
}
复杂度分析
  • 时间复杂度O(n + k)n为元素数量,k为值域范围)。
  • 空间复杂度O(k)
  • 稳定性:稳定(相同值的元素相对顺序不变)。

二、常见变体及代码示例

1. 支持负数的计数排序

改进点:通过偏移量处理负数,扩展适用范围。
适用场景:数据包含负值。

public class CountingSortWithNegatives {
    void sort(int[] arr) {
        if (arr.length == 0) return;

        int min = Arrays.stream(arr).min().getAsInt();
        int max = Arrays.stream(arr).max().getAsInt();
        int range = max - min + 1;

        int[] count = new int[range];
        for (int num : arr) {
            count[num - min]++; // 偏移量为min
        }

        for (int i = 1; i < count.length; 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);
    }
}
2. 原地计数排序

改进点:尝试在原数组上操作,减少额外空间。
适用场景:内存受限场景(但可能牺牲时间效率)。

public class InPlaceCountingSort {
    void sort(int[] arr) {
        if (arr.length == 0) return;

        int min = Arrays.stream(arr).min().getAsInt();
        int max = Arrays.stream(arr).max().getAsInt();
        int range = max - min + 1;

        int[] count = new int[range];
        for (int num : arr) {
            count[num - min]++;
        }

        // 直接在原数组上填充
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i + min;
                count[i]--;
            }
        }
    }
}
3. 并行计数排序

改进点:利用多线程加速计数统计。
适用场景:超大数据集或并行计算环境。

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ParallelCountingSort {
    void sort(int[] arr) {
        if (arr.length == 0) return;

        int min = Arrays.stream(arr).min().getAsInt();
        int max = Arrays.stream(arr).max().getAsInt();
        int range = max - min + 1;
        int[] count = new int[range];

        // 并行统计
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new CountTask(arr, count, 0, arr.length - 1, min));

        // 累加统计结果
        for (int i = 1; i < count.length; 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);
    }

    // 并行统计任务
    private static class CountTask extends RecursiveAction {
        private final int[] arr;
        private final int[] count;
        private final int start;
        private final int end;
        private final int min;

        CountTask(int[] arr, int[] count, int start, int end, int min) {
            this.arr = arr;
            this.count = count;
            this.start = start;
            this.end = end;
            this.min = min;
        }

        @Override
        protected void compute() {
            if (end - start < 1000) { // 小区间直接统计
                for (int i = start; i <= end; i++) {
                    count[arr[i] - min]++;
                }
            } else {
                int mid = (start + end) / 2;
                invokeAll(new CountTask(arr, count, start, mid, min),
                        new CountTask(arr, count, mid + 1, end, min));
            }
        }
    }
}

三、变体对比表格

变体名称时间复杂度空间复杂度稳定性主要特点适用场景
基础计数排序O(n + k)O(k)稳定简单易实现,适用于小范围数据值域较小且无负数的场景
支持负数的计数排序O(n + k)O(k)稳定扩展支持负数,需计算最小值数据包含负值的场景
原地计数排序O(n + k)O(k)稳定减少输出数组的使用,直接修改原数组内存受限但时间允许的场景
并行计数排序O(n/k + k)(并行)O(k)稳定利用多线程加速统计步骤超大数据集或高性能计算环境

四、关键选择原则

  1. 基础场景:优先使用基础计数排序,因其简单且适用于小范围数据。
  2. 负数支持:当数据包含负值时,需使用支持负数的变体。
  3. 内存限制:原地排序适合内存紧张但允许额外统计数组的场景。
  4. 性能优化:并行计数排序适用于超大数据集或并行计算环境。
  5. 稳定性需求:所有变体均稳定,适用于需保持元素相对顺序的场景(如排序带键值的记录)。

通过选择合适的变体,可在特定场景下优化性能或扩展适用性。例如,并行计数排序在大数据集上显著提升速度,而支持负数的变体扩展了算法的通用性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式单片机开发 - Keil MDK 编译与烧录程序

Keil MDK 编译程序 1、Keil MDK 编译按钮 Build 按钮&#xff1a;重新编译整个工程的所有源文件&#xff0c;无论它们是否被修改过 Rebuild 按钮&#xff1a;仅编译修改过的文件及其依赖项&#xff0c;未修改的文件直接使用之前的编译结果 2、Keil MDK 编译结果 linking... …

裂项法、分式分解法——复杂分式的拆解

目录 一、裂项法 1. 核心思想 2. 适用场景 3. 步骤 4. 例题 二、分式分解 1. 核心思想 2. 适用场景 3. 步骤 4.例题 一、裂项法 1. 核心思想 将一项拆解为多项之差&#xff0c;使得在求和时中间项相互抵消&#xff0c;最终仅剩首尾少数项。 2. 适用场景 级数求和…

黑马点评秒杀优化

异步优化秒杀业务 回顾之前的内容黑马点评 秒杀优惠券集群下一人一单超卖问题-CSDN博客&#xff0c;为了处理并发情况下的线程安全和数据一致性的问题&#xff0c;我们已经完成了查询优惠券信息、判断秒杀是否开始和结束、检查库存、用户ID加锁、创建订单和扣减库存。 尽管之前…

JavaScript 的演变:2023-2025 年的新特性解析

随着Web技术的飞速发展&#xff0c;ECMAScript&#xff08;简称ES&#xff09;作为JavaScript的语言标准&#xff0c;也在不断进化。 本文将带你学习 ECMAScript 2023-2025 的新特性。 一、ECMAScript 2023 新特性 1.1 数组的扩展 Array.prototype.findLast()/Array.protot…

[Java · 初窥门径] Java 注释符

&#x1f31f; 想系统化学习 Java 编程&#xff1f;看看这个&#xff1a;[编程基础] Java 学习手册 0x01&#xff1a;Java 注释符简介 在编写程序时&#xff0c;为了使代码易于理解&#xff0c;通常会为代码加一些注释。Java 注释就是用通俗易懂的语言对代码进行描述或解释&a…

Docker环境下SpringBoot程序内存溢出(OOM)问题深度解析与实战调优

文章目录 一、问题背景与现象还原**1. 业务背景****2. 故障特征****3. 核心痛点****4. 解决目标** 二、核心矛盾点分析**1. JVM 与容器内存协同失效****2. 非堆内存泄漏****3. 容器内存分配策略缺陷** 三、系统性解决方案**1. Docker 容器配置**2. JVM参数优化&#xff08;容器…

【计算机网络】网络基础(协议,网络传输流程、Mac/IP地址 、端口号)

目录 1.协议简述2.网络分层结构2.1 软件分层2.2 网络分层为什么&#xff1f; 是什么&#xff1f;OSI七层模型TCP/IP五层&#xff08;或四层&#xff09;结构 3. 网络与操作系统之间的关系4.从语言角度理解协议5.网络如何传输局域网通信&#xff08;同一网段&#xff09; 不同网…

pgsql中使用jsonb的mybatis-plus和jps的配置

在pgsql中使用jsonb类型的数据时&#xff0c;实体对象要对其进行一些相关的配置&#xff0c;而mybatis和jpa中使用各不相同。 在项目中经常会结合 MyBatis-Plus 和 JPA 进行开发&#xff0c;MyBatis_plus对于操作数据更灵活&#xff0c;jpa可以自动建表&#xff0c;两者各取其…

使用MetaGPT 创建智能体(2)多智能体

先给上个文章使用MetaGPT 创建智能体&#xff08;1&#xff09;入门打个补丁&#xff1a; 补丁1&#xff1a; MeteGTP中Role和Action的关联和区别&#xff1f;这是这两天再使用MetaGPT时候心中的疑问&#xff0c;这里做个记录 Role&#xff08;角色&#xff09;和 Action&…

C# 使用.NET内置的 IObservable<T> 和 IObserver<T>-观察者模式

核心概念 IObservable<T> 表示 可观察的数据源&#xff08;如事件流、实时数据&#xff09;。 关键方法&#xff1a;Subscribe(IObserver<T> observer)&#xff0c;用于注册观察者。 IObserver<T> 表示 数据的接收者&#xff0c;响应数据变化。 三个核心…

Redis——网络模型之IO讲解

目录 前言 1.用户空间和内核空间 1.2用户空间和内核空间的切换 1.3切换过程 2.阻塞IO 3.非阻塞IO 4.IO多路复用 4.1.IO多路复用过程 4.2.IO多路复用监听方式 4.3.IO多路复用-select 4.4.IO多路复用-poll 4.5.IO多路复用-epoll 4.6.select poll epoll总结 4.7.IO多…

vue3 传参 传入变量名

背景&#xff1a; 需求是&#xff1a;在vue框架中&#xff0c;接口传参我们需要穿“变量名”&#xff0c;而不是字符串 通俗点说法是&#xff1a;在网络接口请求的时候&#xff0c;要传属性名 效果展示&#xff1a; vue2核心代码&#xff1a; this[_keyParam] vue3核心代码&…

旅游特种兵迪士尼大作战:DeepSeek高精准路径优化

DeepSeek大模型高性能核心技术与多模态融合开发 - 商品搜索 - 京东 随着假期的脚步日渐临近&#xff0c;环球影城等备受瞩目的主题游乐场&#xff0c;已然成为大人与孩子们心中不可或缺的节日狂欢圣地。然而&#xff0c;随之而来的庞大客流&#xff0c;却总让无数游客在欢乐的…

【MySQL】第一弹——MySQL数据库结构与操作

目录 一. 数据库介绍1.1 什么是数据库1.2 为什么要使用数据库1.3 主流数据库1.3.1 关系型数据库1.3.2 非关系型数据库 二. MySQL 的结构2.1 MySQL服务器和客户端2.2 MySQL服务器是如何组织数据的 三. 数据库的操作3.1 创建数据库语法格式示例 3.2 查看数据库语法格式示例 3.3 使…

Spring_MVC 快速入门指南

Spring_MVC 快速入门指南 一、Spring_MVC 简介 1. 什么是 Spring_MVC&#xff1f; Spring_MVC 是 Spring 框架的一个模块&#xff0c;用于构建 Web 应用程序。它基于 MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;将应用程序分为模型&#xff08;M…

L38.【LeetCode题解】四数之和(双指针思想) 从汇编角度分析报错原因

目录 1.题目 2.分析 去重的代码 错误代码 3.完整代码 提交结果 1.题目 四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元…

字符串系列一>最长回文子串

目录 题目&#xff1a;解析&#xff1a;代码&#xff1a; 题目&#xff1a; 链接: link 解析&#xff1a; 代码&#xff1a; class Solution {public String longestPalindrome(String s) {char[] ss s.toCharArray();int n ss.length;int begin 0;//返回结果的起始字符串…

双击热备方案及不同方案的需求、方案对比

双击热备方案概述 一般实现双机热备的方案有三种,分别是共享存储双机热备方案、镜像双机热备方案、双机双柜双机热备方案,这三种方案对硬件要求不同,大家可以根据自身的业务应用特性来选择具体的双机热备方案以及对应的ServHA双机热备软件产品。 1、镜像双机热备 1.1镜像…

VSCODE插值表达式失效问题

GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_-CSDN博客 更换正确的vue域名 GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_ <script src"https://unpkg.com/vue2.6.14/dist/vue.js"></sc…

【失败】Gnome将默认终端设置为 Kitty

起因 一会儿gnome-terminal一会儿kitty终端&#xff0c;实在是受不了&#xff0c;决定取缔默认的gnome-terminal。 过程 在 Ubuntu 或 Debian 系统上&#xff1a; 确保 Kitty 已经安装。如果未安装&#xff0c;可以在终端中运行命令sudo apt install kitty -y进行安装。 使用系…