排序算法之桶排序

目录

  • 一、简介
  • 二、代码实现
  • 三、应用场景


一、简介

算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度排序方式稳定性
桶排序O(n+k )O(n+k)O(n^2)O(n+k)Out-place稳定

稳定:如果A原本在B前面,而A=B,排序之后A仍然在B的前面;
不稳定:如果A原本在B的前面,而A=B,排序之后A可能会出现在B的后面;
时间复杂度: 描述一个算法执行所耗费的时间;
空间复杂度:描述一个算法执行所需内存的大小;
n:数据规模;
k:“桶”的个数;
In-place:占用常数内存,不占用额外内存;
Out-place:占用额外内存。

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,需要做到这两点:

在额外空间充足的情况下,尽量增大桶的数量
使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
什么时候最快?
当输入的数据可以均匀的分配到每一个桶中。
什么时候最慢?
当输入的数据被分配到了同一个桶中。

算法步驟:

  • 1.确定桶的数量:根据输入数据的范围和数量确定需要多少个桶。
    首先确定桶的个数。因为桶排序最好是将数据均匀地分散在各个桶中,那么桶的个数最好是应该根据数据的分散情况来确定。首先找出所有数据中的最大值mx和最小值mn;
    根据mx和mn确定每个桶所装的数据的范围 size,有
    size = (mx - mn) / n + 1,n为数据的个数,需要保证至少有一个桶,故而需要加个1;
    求得了size即知道了每个桶所装数据的范围,还需要计算出所需的桶的个数cnt,有
    cnt = (mx - mn) / size + 1,需要保证每个桶至少要能装1个数,故而需要加个1;
  • 2.将数据分配到各个桶中:遍历输入数据,根据数据的值将数据分配到对应的桶中。
    求得了size和cnt后,即可知第一个桶装的数据范围为 [mn, mn + size),第二个桶为 [mn + size, mn + 2 * size),…,以此类推,因此步骤2中需要再扫描一遍数组,将待排序的各个数放进对应的桶中。
  • 3.对每个桶内的数据进行排序:对每个非空的桶内的数据进行排序,可以使用插入排序等方法。
  • 4.合并各个桶的数据:按照桶的顺序将各个桶内的数据合并为最终的排序结果。

示意图
元素分布在桶中:
在这里插入图片描述
然后,元素在每个桶中排序:
在这里插入图片描述


二、代码实现

 public void bucketSort(int[] nums) {int n = nums.length;int mn = nums[0], mx = nums[0];// 找出数组中的最大最小值for (int i = 1; i < n; i++) {mn = Math.min(mn, nums[i]);mx = Math.max(mx, nums[i]);}int size = (mx - mn) / n + 1; // 每个桶存储数的范围大小,使得数尽量均匀地分布在各个桶中,保证最少存储一个int cnt = (mx - mn) / size + 1; // 桶的个数,保证桶的个数至少为1List<Integer>[] buckets = new List[cnt]; // 声明cnt个桶for (int i = 0; i < cnt; i++) {buckets[i] = new ArrayList<>();//每一个桶都是一个集合}// 扫描一遍数组,将数放进桶里for (int i = 0; i < n; i++) {int idx = (nums[i] - mn) / size;buckets[idx].add(nums[i]);}// 对各个桶中的数进行排序,这里用库函数快速排序for (int i = 0; i < cnt; i++) {buckets[i].sort(null); // 默认是按从小打到排序}// 依次将各个桶中的数据放入返回数组中int index = 0;for (int i = 0; i < cnt; i++) {for (int j = 0; j < buckets[i].size(); j++) {nums[index++] = buckets[i].get(j);}}}

demo示例一下

public class BucketSort {public static void bucketSort(int[] nums) {int n = nums.length;int mn = nums[0], mx = nums[0];// 找出数组中的最大最小值for (int i = 1; i < n; i++) {mn = Math.min(mn, nums[i]);mx = Math.max(mx, nums[i]);}int size = (mx - mn) / n + 1; // 每个桶存储数的范围大小,使得数尽量均匀地分布在各个桶中,保证最少存储一个System.out.println("每个桶的储存数量大小 " + size);int cnt = (mx - mn) / size + 1; // 桶的个数,保证桶的个数至少为1System.out.println("桶的个数 " + cnt);List<Integer>[] buckets = new List[cnt]; // 声明cnt个桶for (int i = 0; i < cnt; i++) {buckets[i] = new ArrayList<>();//每一个桶都是一个集合}for (int i = 0; i < n; i++) {int number1 = mn + i * size;int number2 = mn + (i + 1) * size;System.out.println("第" + i + "个桶存理应存放数据范围 " + number1 + "----" + number2);}// 扫描一遍数组,将数放进桶里for (int i = 0; i < n; i++) {int idx = (nums[i] - mn) / size;buckets[idx].add(nums[i]);System.out.println("第" + idx + "个桶存放了" + nums[i]);}// 对各个桶中的数进行排序,这里用库函数快速排序for (int i = 0; i < cnt; i++) {buckets[i].sort(null); // 默认是按从小打到排序}// 依次将各个桶中的数据放入返回数组中int index = 0;for (int i = 0; i < cnt; i++) {for (int j = 0; j < buckets[i].size(); j++) {nums[index++] = buckets[i].get(j);}}}public static void bucketSort2(int[] nums) {int n = nums.length;int mn = nums[0], mx = nums[0];// 找出数组中的最大最小值for (int i = 1; i < n; i++) {mn = Math.min(mn, nums[i]);mx = Math.max(mx, nums[i]);}int size = (mx - mn) / n + 1; // 每个桶存储数的范围大小,使得数尽量均匀地分布在各个桶中,保证最少存储一个int cnt = (mx - mn) / size + 1; // 桶的个数,保证桶的个数至少为1List<Integer>[] buckets = new List[cnt]; // 声明cnt个桶for (int i = 0; i < cnt; i++) {buckets[i] = new ArrayList<>();}// 扫描一遍数组,将数放进桶里for (int i = 0; i < n; i++) {int idx = (nums[i] - mn) / size;buckets[idx].add(nums[i]);}// 对各个桶中的数进行排序,这里用库函数快速排序for (int i = 0; i < cnt; i++) {buckets[i].sort(new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return b - a; // 从大到小排序}});}// 依次将各个桶中的数据放入返回数组中int index = 0;for (int i = cnt - 1; i >= 0; i--) {for (int j = 0; j < buckets[i].size(); j++) {nums[index++] = buckets[i].get(j);}}}public static void main(String[] args) {int[] nums = {12, 11, 15, 50, 7, 65, 3, 99};System.out.println("排序前 :" + Arrays.toString(nums));bucketSort(nums);System.out.println("桶排序从小到大排序后 :" + Arrays.toString(nums));bucketSort2(nums);System.out.println("桶排序从大到小排序后 :" + Arrays.toString(nums));}
}

在这里插入图片描述

三、应用场景

桶排序适用于数据量较大且分布较均匀的情况,可以在一定程度上提高排序的效率。

  • 排序整数或浮点数: 桶排序适用于排序整数或浮点数,特别是在这些数的范围不是很大的情况下。通过将数分配到不同的桶中,可以在每个桶内使用其他排序算法(如插入排序、快速排序)来对桶内的数进行排序,最终将桶中的数合并得到有序序列。
  • 均匀分布的数据: 如果输入数据是均匀分布的,即数据在不同的范围内基本均匀分布,那么桶排序的效果会很好。因为桶排序将数据分配到不同的桶中,可以保证数据在各个桶中基本均匀分布,从而减少排序的复杂度。
  • 外部排序: 桶排序也适用于外部排序,即当数据量非常大,无法一次性加载到内存中进行排序时。在外部排序中,可以将数据分成若干块,每块数据可以加载到内存中进行桶排序,然后将排序后的数据写回磁盘,最终将所有块合并得到有序序列。
  • 计数排序的扩展: 桶排序是计数排序的一种扩展,适用于更大范围的数据。当计数排序不适用于数据范围很大的情况时,桶排序可以更好地处理这种情况。

桶排序的关键点:

  • 确定桶的数量和大小:在进行桶排序之前,需要确定桶的数量和大小。桶的数量可以根据待排序数据的范围来确定,而桶的大小可以根据数据的分布情况来选择。通常情况下,桶的数量可以取数据个数的平方根或者数据范围的大小。
  • 数据分配到桶中:一旦确定了桶的数量和大小,接下来需要将待排序数据分配到对应的桶中。这通常涉及到计算每个数据在哪个桶内,可以根据数据的大小和桶的范围来确定。
  • 对每个非空桶内的数据进行排序:在桶排序中,每个非空桶内的数据需要进行排序。这可以使用任意一种排序算法,如插入排序、快速排序等。在实际应用中,通常选择适合桶内数据规模的排序算法。
  • 合并各个桶的数据:最后一步是将各个桶内的数据按照顺序合并为最终的排序结果。这通常涉及遍历所有桶,按照桶的顺序将数据合并起来。
  • 处理边界情况:在实现桶排序时,需要考虑处理一些边界情况,例如空桶的情况、数据分布不均匀的情况等。这些情况可能会影响桶排序的性能和正确性。

参考链接:
十大经典排序算法(Java实现)

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

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

相关文章

Kotlin语法快速入门--条件控制和循环语句(2)

Kotlin语法入门–条件控制和循环语句&#xff08;2&#xff09; 文章目录 Kotlin语法入门--条件控制和循环语句&#xff08;2&#xff09;二、条件控制和循环语句1、if...else2、when2.1、常规用法2.2、特殊用法--并列&#xff1a;2.3、特殊用法--类型判断&#xff1a;2.4、特殊…

C语言进阶课程学习记录-第48课 - 函数设计原则

C语言进阶课程学习记录 - 函数设计原则 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录

无人驾驶 自动驾驶汽车 环境感知 精准定位 决策与规划 控制与执行 高精地图与车联网V2X 深度神经网络学习 深度强化学习 Apollo

无人驾驶 百度apollo课程 1-5 百度apollo课程 6-8 七月在线 无人驾驶系列知识入门到提高 当今,自动驾驶技术已经成为整个汽车产业的最新发展方向。应用自动驾驶技术可以全面提升汽车驾驶的安全性、舒适性,满足更高层次的市场需求等。自动驾驶技术得益于人工智能技术的应用…

端口被占用的解决方案汇总

端口被占用的解决方案汇总 【一】windows系统端口被占用【二】Linux系统端口被占用【三】Linux的ps命令查找&#xff08;1&#xff09;ps命令常用的方式有三种&#xff08;2&#xff09;ps -ef |grep 8080 【一】windows系统端口被占用 &#xff08;1&#xff09;键盘上按住Wi…

【LeetCode刷题记录】21. 合并两个有序链表

21 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 …

# MySQL中的并发控制,读写锁,和锁的粒度

MySQL中的并发控制&#xff0c;读写锁&#xff0c;和锁的粒度 并发控制的概述 在数据库系统中&#xff0c;并发控制是一种用于确保当多个用户同时访问数据库时&#xff0c;系统能够提供数据的一致性和隔离性的机制。MySQL支持多种并发控制技术&#xff0c;其中包括锁机制、多…

调试 WebSocket API 技巧分享

WebSocket 是一种在单个 TCP 连接上实现全双工通信的先进 API 技术。与传统的 HTTP 请求相比&#xff0c;WebSocket 提供了更低的延迟和更高的通信效率&#xff0c;使其成为在线游戏、实时聊天等应用的理想选择。 开始使用 Apifox 的 WebSocket 功能 首先&#xff0c;在项目界…

node和go的列表转树形, 执行速度测试对比

保证数据一致性&#xff0c;先生成4000条json数据到本地&#xff0c;然后分别读取文本执行处理 node代码 node是用midway框架 forNum1:number 0forNum2:number 0//执行测试async index(){// 生成菜单列表// const menuList await this.generateMenuList([], 4000);const men…

双周总结#008 - AIGC

本周参与了公司同事对 AIGC 的分享会&#xff0c;分享了 AIGC 在实际项目中的实践经验&#xff0c;以及如何进行 AIGC 的落地。内容分几项内容&#xff1a; 什么是 AIGCAIGC 能做什么AIGC 工具 以年终总结为例&#xff0c;分享了哪些过程应用了 AIGC&#xff0c;以及 AIGC 落地…

QA测试开发工程师面试题满分问答19: url请求到响应整个过程,涉及到什么技术细节

概述 当你点击鼠标发起一个请求&#xff0c;直到页面显示响应数据&#xff0c;整个过程可以详细展开为以下步骤&#xff1a; 用户点击鼠标&#xff1a;用户在浏览器中点击某个链接或按钮&#xff0c;触发请求的发起。 URL 解析&#xff1a;浏览器解析点击的链接中的 URL&…

在线音乐播放网站项目测试(selenium+Junit5)

在做完在线音乐播放网站项目之后&#xff0c;需要对项目的功能、接口进行测试&#xff0c;利用测试的工具&#xff1a;selenium以及Java的单元测试工具Junit进行测试&#xff0c;下面式测试的思维导图&#xff0c;列出该项目需要测试的所有测试用例&#xff1a; 测试结果&#…

下列程序定义了NxN的二维数组,并在主函数中自动赋值。请编写函数fun(int a[][N],int n),该函数的功能是:使数组右上半三角元素中的值乘以m。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 下列…

【QML】State组件

State(状态)组件是一组来自默认配置的批处理更改。所有项都有一个默认状态&#xff0c;该状态定义对象和属性值的默认配置。可以通过将State项添加到states属性来定义新的状态&#xff0c;以允许项在不同的配置之间切换。 State组件的基本用法如下&#xff1a; Window {id: …

【ARM Trace32(劳特巴赫) 使用介绍 12.1 -- Trace32 读写 64位地址】

请阅读【Trace32 ARM 专栏导读】 文章目录 Trace32 读写 64位地址读 64 位地址写64位地址Trace32 读写 64位地址 在使用TRACE32进行调试时,有时需要读取或操作64位的地址,特别是在处理64位的处理器或操作系统时。以下是如何在TRACE32中读取64位地址的一般方法。 读 64 位地…

MySQL行级锁——技术深度+1

引言 本文是对MySQL行级锁的学习&#xff0c;MySQL一直停留在会用的阶段&#xff0c;需要弄清楚锁和事务的原理并DEBUG查看。 PS:本文涉及到的表结构均可从https://github.com/WeiXiao-Hyy/blog中获取&#xff0c;欢迎Star&#xff01; MySQL行级锁 行级锁&#xff08;Row-…

js连接抖音打印组件实现打印

js连接抖音打印组件实现打印小票 安装抖音打印组件 抖音打印组件文档&#xff1a; https://bytedance.larkoffice.com/docs/doccn2vbOOdd3KWrCd6Z93nIlvg 跟着文档案例一步步配基本上没问题&#xff0c; 打印的时候需要设置下打印机名称 export class DouyinPrint {construct…

怎么理解算力?1000P算力是什么概念?

算力&#xff0c;指计算机系统在单位时间内能够完成的计算任务量&#xff0c;它涵盖了CPU、GPU、TPU等硬件&#xff0c;每秒能处理的数据量&#xff0c;通常以“P”&#xff08;PetaFLOPS&#xff0c;即千万亿次浮点运算每秒&#xff09;为单位来衡量&#xff0c;是评估计算机性…

PDF被加密无法打印的解决办法

思路很清晰&#xff1a;先解密→再打印 分享四个工具&#xff0c;可以轻松解密PDF&#xff1a; ⭐i love pdf I LOVE PDF是一款免费的PDF网站&#xff0c;界面设计简洁&#xff0c;首页没有广告&#xff0c;但每个功能的操作界面是有广告的&#xff0c;不会影响使用。 部分功…

大数据开发详解

点击下载《大数据开发详解》 1. 前言 随着信息化时代的快速发展&#xff0c;大数据已经成为了企业和组织不可或缺的重要资源。大数据开发则是指通过一系列技术手段&#xff0c;对海量数据进行收集、存储、处理、分析和挖掘&#xff0c;以实现数据的价值化利用。大数据开发涉及…

剑指offer--调整数字顺序使奇数位于偶数前面

题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有的偶数位于数组的后半部分. 算法分析 算法:利用快速排序的一次划分思想&#xff0c;后面的奇数往前移&#xff0c;前面的偶数往后移 时间复杂度 &#xff1a;O(n) 空间…