【数据结构】排序算法---桶排序

在这里插入图片描述

文章目录

  • 1. 定义
  • 2. 算法步骤
  • 3. 演示
    • 3.1 动态演示1
    • 3.2 动态演示2
    • 3.3 图片演示1
    • 3.4 图片演示2
  • 4. 性质
  • 5. 算法分析
  • 6. 代码实现
    • C语言
    • Python
    • Java
    • C++
    • Go
  • 结语

1. 定义

桶排序(英文:Bucket sort)是计数排序的升级版,适用于待排序数据值域较大但分布比较均匀的情况。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

为了使桶排序更加高效,我们需要做到这两点:

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

同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

2. 算法步骤

桶排序按下列步骤进行:

  1. 设置一个定量的数组当作空桶;

  2. 遍历输入数据,并且把数据一个一个放到对应的桶里去;

  3. 对每个不是空的桶进行排序;

  4. 从不是空的桶里把排好序的数据拼接起来。

3. 演示

3.1 动态演示1

在这里插入图片描述

3.2 动态演示2

在这里插入图片描述

3.3 图片演示1

元素分布在桶中:

在这里插入图片描述

然后,元素在每个桶中排序:

在这里插入图片描述

3.4 图片演示2

在这里插入图片描述

4. 性质

稳定性

如果使用稳定的内层排序,并且将元素插入桶中时不改变元素间的相对顺序,那么桶排序就是一种稳定的排序算法。

由于每块元素不多,一般使用插入排序。此时桶排序是一种稳定的排序算法。

空间复杂度

桶排序算法排序过程中新建了一个桶和一个输出数组,所以算法的空间复杂度是 O ( N + M ) O(N+M) O(N+M)

时间复杂度

桶排序的平均时间复杂度为 O ( n + n 2 k + k ) O(n+{n^2 \over k}+k) O(n+kn2+k)(将值域平均分成n块 + 排序 + 重新合并元素),当k≈n时为 O ( n ) O(n) O(n)

桶排序的最坏时间复杂度为 O ( n 2 ) O(n^2) O(n2)

什么时候最快

当输入的数据可以均匀的分配到每一个桶中。

什么时候最慢

当输入的数据被分配到了同一个桶中。

5. 算法分析

桶排序最好情况下使用线性时间 O ( n ) O(n) O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为 O ( n ) O(n) O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

6. 代码实现

C语言

void RadixSort(int* arr, int n)
{//max为数组中最大最小值int max = arr[0];int min = arr[0];int base = 1;//找出数组中的最大值for (int i = 0; i < n; i++){if (arr[i] > max){max = arr[i];}if (arr[i] < min){min = arr[i];}}//循环结束max就是数组最大最小值//循环将数组的元素全部变为正数//所有元素加上最小值的绝对值for (int i = 0; i < n; i++){arr[i] += abs(min);}//临时存放数组元素的空间int* tmp = (int*)malloc(sizeof(int)*n);//循环次数为最大数的位数while (max / base > 0){//定义十个桶,桶里面装的不是数据本身,而是每一轮排序对应(十、白、千...)位的个数//统计每个桶里面装几个数int bucket[10] = { 0 };for (int i = 0; i < n; i++){//arr[i] / base % 10可以取到个位、十位、百位对应的数字bucket[arr[i] / base % 10]++;}//循环结束就已经统计好了本轮每个桶里面应该装几个数//将桶里面的元素依次累加起来,就可以知道元素存放在临时数组中的位置for (int i = 1; i < 10; i++){bucket[i] += bucket[i - 1];}//循环结束现在桶中就存放的是每个元素应该存放到临时数组的位置//开始放数到临时数组tmpfor (int i = n - 1; i >= 0; i--){tmp[bucket[arr[i] / base % 10] - 1] = arr[i];bucket[arr[i] / base % 10]--;}//不能从前往后放,因为这样会导致十位排好了个位又乱了,百位排好了十位又乱了/*for (int i = 0; i < n; i++){tmp[bucket[arr[i] / base % 10] - 1] = arr[i];bucket[arr[i] / base % 10]--;}*///把临时数组里面的数拷贝回去for (int i = 0; i < n; i++){arr[i] = tmp[i];}base *= 10;}free(tmp);//还原原数组for (int i = 0; i < n; i++){arr[i] -= abs(min);}
}

Python

N = 100010
w = n = 0
a = [0] * N
bucket = [[] for i in range(N)]def insertion_sort(A):for i in range(1, len(A)):key = A[i]j = i - 1while j >= 0 and A[j] > key:A[j + 1] = A[j]j -= 1A[j + 1] = keydef bucket_sort():bucket_size = int(w / n + 1)for i in range(0, n):bucket[i].clear()for i in range(1, n + 1):bucket[int(a[i] / bucket_size)].append(a[i])p = 0for i in range(0, n):insertion_sort(bucket[i])for j in range(0, len(bucket[i])):a[p] = bucket[i][j]p += 1

Java

public class BucketSort extends BaseSort {public static void main(String[] args) {BucketSort sort = new BucketSort();sort.printNums();}@Overrideprotected void sort(int[] nums) {if (nums == null || nums.length < 2) {return;}bucketSort(nums);}public void bucketSort(int[] nums) {if (nums == null || nums.length < 2) {return;}//找出最大值,最小值int max = Integer.MIN_VALUE;int min = Integer.MAX_VALUE;for (int num : nums) {min = Math.min(min, num);max = Math.max(max, num);}int length = nums.length;//桶的数量int bucketCount = (max - min) / length + 1;int[][] bucketArrays = new int[bucketCount][];//遍历数组,放入桶内for (int i = 0; i < length; i++) {//找到桶的下标int index = (nums[i] - min) / length;//添加到指定下标的桶里,并且使用插入排序排序bucketArrays[index] = insertSortArrays(bucketArrays[index], nums[i]);}int k = 0;//合并全部桶的for (int[] bucketArray : bucketArrays) {if (bucketArray == null || bucketArray.length == 0) {continue;}for (int i : bucketArray) {//把值放回到nums数组中nums[k++] = i;}}}//每个桶使用插入排序进行排序private int[] insertSortArrays(int[] arr, int num) {if (arr == null || arr.length == 0) {return new int[]{num};}//创建一个temp数组,长度是arr数组的长度+1int[] temp = new int[arr.length + 1];//把传进来的arr数组,复制到temp数组for (int i = 0; i < arr.length; i++) {temp[i] = arr[i];}//找到一个位置,插入,形成新的有序的数组int i;for (i = temp.length - 2; i >= 0 && temp[i] > num; i--) {temp[i + 1] = temp[i];}//插入需要添加的值temp[i + 1] = num;//返回return temp;}
}

C++

#include<iterator>
#include<iostream>
#include<vector>
using namespace std;
const int BUCKET_NUM = 10;struct ListNode{explicit ListNode(int i=0):mData(i),mNext(NULL){}ListNode* mNext;int mData;
};ListNode* insert(ListNode* head,int val){ListNode dummyNode;ListNode *newNode = new ListNode(val);ListNode *pre,*curr;dummyNode.mNext = head;pre = &dummyNode;curr = head;while(NULL!=curr && curr->mData<=val){pre = curr;curr = curr->mNext;}newNode->mNext = curr;pre->mNext = newNode;return dummyNode.mNext;
}ListNode* Merge(ListNode *head1,ListNode *head2){ListNode dummyNode;ListNode *dummy = &dummyNode;while(NULL!=head1 && NULL!=head2){if(head1->mData <= head2->mData){dummy->mNext = head1;head1 = head1->mNext;}else{dummy->mNext = head2;head2 = head2->mNext;}dummy = dummy->mNext;}if(NULL!=head1) dummy->mNext = head1;if(NULL!=head2) dummy->mNext = head2;return dummyNode.mNext;
}void BucketSort(int n,int arr[]){vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));for(int i=0;i<n;++i){int index = arr[i]/BUCKET_NUM;ListNode *head = buckets.at(index);buckets.at(index) = insert(head,arr[i]);}ListNode *head = buckets.at(0);for(int i=1;i<BUCKET_NUM;++i){head = Merge(head,buckets.at(i));}for(int i=0;i<n;++i){arr[i] = head->mData;head = head->mNext;}
}

Go

package mainimport ("fmt""container/list"
)func bucketSort(theArray []int,num int){var theSort [99]intfor i:=0;i< len(theArray);i++{theSort[10]=1if theSort[theArray[i]] !=0{theSort[theArray[i]] = theSort[theArray[i]]+1}else{theSort[theArray[i]] = 1}}l:=list.New()for j:=0;j<len(theSort);j++{if theSort[j]==0{//panic("error test.....")}else{for k:=0;k<theSort[j];k++{l.PushBack(j)}}}for e := l.Front(); e != nil; e = e.Next() {fmt.Print(e.Value, " ")}}func main() {var theArray = []int{10, 1, 18, 30, 23, 12, 7, 5, 18, 17}fmt.Print("排序前")fmt.Println(theArray)fmt.Print("排序后")bucketSort(theArray,11)
}

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

带你初步了解排序算法:https://blog.csdn.net/2301_80191662/article/details/142211265
直接插入排序:https://blog.csdn.net/2301_80191662/article/details/142300973
希尔排序:https://blog.csdn.net/2301_80191662/article/details/142302553
直接选择排序:https://blog.csdn.net/2301_80191662/article/details/142312028
堆排序:https://blog.csdn.net/2301_80191662/article/details/142312338
冒泡排序:https://blog.csdn.net/2301_80191662/article/details/142324131
快速排序:https://blog.csdn.net/2301_80191662/article/details/142324307
归并排序:https://blog.csdn.net/2301_80191662/article/details/142350640
计数排序:https://blog.csdn.net/2301_80191662/article/details/142350741
桶排序:https://blog.csdn.net/2301_80191662/article/details/142375338
基数排序:https://blog.csdn.net/2301_80191662/article/details/142375592
十大经典排序算法总结与分析:https://blog.csdn.net/2301_80191662/article/details/142211564

在这里插入图片描述

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

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

相关文章

Elasticsearch黑窗口启动乱码问题解决方案

问题描述 elasticsearch启动后有乱码现象 解决方案&#xff1a; 提示&#xff1a;这里填写该问题的具体解决方案&#xff1a; 到 \config 文件下找到 jvm.options 文件 打开后 在文件末尾空白处 添加 -Dfile.encodingGBK 保存后重启即可。

1. Linux系统(CentOS7.9)安装

toc 一、Linux概述介绍 1、Linux系统介绍 Linux, 一类操作系统的统称 部署在服务器上&#xff0c;部署项目、应用 服务器: 硬件设备, 柜式服务器&#xff0c;(华为、浪潮、联想) 提供服务的机器 2、Linux的优势 开源, open source , 开放源代码稳定性最大化发挥硬件资源 …

微服务注册中⼼1

1. 微服务的注册中⼼ 注册中⼼可以说是微服务架构中的”通讯录“ &#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c; 服务会注册到这⾥&#xff0c;当服务需要调⽤其它服务时&#xff0c;就这⾥找到服务的地址&#xff0c;进⾏调⽤。 1.1 注册中⼼的…

【Redis入门到精通七】详解Redis持久化机制(AOF,RDB)

目录 Redis持久化机制 1.RDB持久化 &#xff08;1&#xff09;手动触发RDB持久化 &#xff08;2&#xff09;自动触发RDB持久化 &#xff08;3&#xff09;Redis文件相关处理 &#xff08;4&#xff09;RDB持久化的优缺点 2.AOF持久化 &#xff08;1&#xff09;AOF工作…

【隐私计算篇】利用多方安全计算MPC实现VGG16人脸识别隐私推理

1. 背景介绍 本文主要介绍一种利用多方安全计算MPC技术&#xff0c;实现VGG16的人脸识别模型&#xff0c;侧重于模型推理阶段&#xff0c;目前已经公开专利&#xff0c;因此以下内容的分享都是基于公开材料。该分享涉及到最小化多方安全计算(MPC)以及明密文混合计算的思想&…

签署《AI安全国际对话威尼斯共识》 智源持续推动人工智能安全发展

近日&#xff0c;由AI安全国际论坛&#xff08;Safe AI Forum&#xff09;和博古睿研究院&#xff08;Berggruen Institute) 共同举办的第三届国际AI安全对话&#xff08;International Dialogues on AI Safety&#xff09;在威尼斯举办。图灵奖得主Yoshua Bengio、姚期智教授&…

UBUNTU20.04安装CH384串口卡驱动

继续上文&#xff1a;统信UOS安装CH384串口卡驱动-CSDN博客 统信UOS系统成功安装CH384串口驱动后&#xff0c;继续在ubuntu20.04下安装驱动&#xff0c;发现一直报错&#xff0c;原因是内核驱动不一致。 解决办法&#xff1a; 1. 下载最新的驱动。CH35XCH384驱动源文件资源-C…

Java语言程序设计基础篇_编程练习题**18.30 (找出单词)

题目&#xff1a;**18.30 (找出单词) 编写一个程序&#xff0c;递归地找出某个目录下的所有文件中某个单词出现的次数。从命令行如下传递参数&#xff1a; java Exercise18_30 dirName word 习题思路 &#xff08;读取路径方法&#xff09;和18.28题差不多&#xff0c;把找…

Structure-Aware Transformer for Graph Representation Learning

Structure-Aware Transformer for Graph Representation Learning&#xff08;ICML22&#xff09; 摘要 Transformer 架构最近在图表示学习中受到越来越多的关注&#xff0c;因为它通过避免严格的结构归纳偏差而仅通过位置编码对图结构进行编码&#xff0c;自然地克服了图神经…

分享课程:VUE数据可视化教程

在当今这个数据驱动的世界中&#xff0c;数据可视化已经成为了一种至关重要的工具&#xff0c;它帮助我们理解复杂的数据集&#xff0c;发现模式、趋势和异常。数据可视化不仅仅是将数字转换成图表&#xff0c;它是一种将数据转化为洞察力的艺术。 1.什么是数据可视化&#xf…

C语言指针系列1——初识指针

祛魅&#xff1a;其实指针这块儿并不难&#xff0c;有人说难只是因为基础到进阶没有处理好&#xff0c;大家要好好跟着一步一步学习&#xff0c;今天我们先来认识一下指针 指针定义&#xff1a;指针就是内存地址&#xff0c;指针变量是用来存放内存地址的变量&#xff0c;在同一…

Java.动态代理

1.创建一个接口 package Mydynamicproxy1;public interface Star {public abstract String sing(String str);public abstract void dance(String str); }2.创建一个BigStar类&#xff0c;要实现Star这个接口 package Mydynamicproxy1;public class BigStar implements Star{…

webpack4 target:“electron-renderer“ 打包加速配置

背景 昨天写得一篇Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题-CSDN博客文章浏览阅读754次&#xff0c;点赞19次&#xff0c;收藏11次。因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vu…

tcp、udp通信调试工具Socket Tool

tcp、udp通信调试工具Socket Tool ]

线程池的执行流程和配置参数总结

一、线程池的执行流程总结 提交线程任务&#xff1b;如果线程池中存在空闲线程&#xff0c;则分配一个空闲线程给任务&#xff0c;执行线程任务&#xff1b;线程池中不存在空闲线程&#xff0c;则线程池会判断当前线程数是否超过核心线程数&#xff08;corePoolSize&#xff09…

LeaferJS 动画、状态、过渡、游戏框架

LeaferJS 现阶段依然专注于绘图、交互和图形编辑场景。我们引入游戏场景&#xff0c;只是希望让 LeaferJS 被更多有需要的人看到&#xff0c;以充分发挥它的价值 LeaferJS 为你带来了全新的游戏、动画、状态和过渡功能&#xff0c;助你实现那些年少时的游戏梦想。我们引入了丰富…

【后端开发】JavaEE初阶——计算机是如何工作的???

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解计算机工作原理&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;【MySQL】MySQL中JDBC编程——MySQL驱动包安装——&#xff08;超详解&#xff09; &#x1f308;感兴趣的小伙伴看一看小编主…

Dependency Check:一款针对应用程序依赖组件的安全检测工具

关于Dependency Check Dependency-Check 是一款软件组合分析 &#xff08;SCA&#xff09; 工具&#xff0c;可尝试检测项目依赖项中包含的公开披露的漏洞。它通过确定给定依赖项是否存在通用平台枚举 &#xff08;CPE&#xff09; 标识符来实现此目的。如果找到&#xff0c;它…

Java线程的三大特性

原子性: 线程任务不可再分。 int i 1; i; 原子类 AtomicXXX 可见性: 线程之间的操作是互相不可见的。 volatile //线程A如果&#xff0c;flag为true&#xff0c;就运行打印语句 A: trueThread t1 new Thread(() -> {while (true) {if (flag) {System.out.println(&q…

【数据结构】AVL树相关知识详细梳理

1. AVL树的概念 AVL的全称是Adelson-Velsky-Landis&#xff0c;其名称来源于其发明者Adelson、Velsky和Landis&#xff0c; 是平衡二叉树搜索树。 它的出现是由于二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找…