当前位置: 首页 > news >正文

基数(桶)排序算法详解之C语言版

一、算法原理

基数排序算法又称桶排序,是一种原理简单实现相对麻烦一点的算法。基数排序属于稳定排序法,适用于数值比较大的数据之间的排序。
常见的内部排序算法中都使用到了元素之间的比较大小,而基数排序算法不涉及元素之间的比较,而是根据基数将数据存放到相应的“桶”里,经过多趟这样的存放过程,也就是一个渐进排序过程,就可以实现对一组散乱数据进行排序。总趟数是数组中位数最多的那个元素的位数。其实读到这里,大家是不是有一种很熟悉的感觉,没错,这跟创建Hash(散列)表的原理类似。
第一趟排序的基数是1,即从“个位”上的数字开始,将根据每一个元素的“个位”数字将该元素存到到与数字相同的“桶里”,得到一次排序结果。第二趟排序的基数是10,在第一趟排序的基础上,根据“百位”上的数字将元素重新放入相应的“桶”里,得到新一轮的排序。依次类推,直到所有元素中的最高位已经处理完毕,结束整个排序过程。从这里就可以看出基数排序就是一个渐进的排序过程。
需要注意的是使用静态数组进行基数排序时,并不是真的把元素放入“桶”里,而是把某个“位”上数字相同的元素个数存放入“桶”里。这样做是为了方便排序。“桶”的个数是10,这是因为阿拉伯数字就是10个,分别是0,1,2,3,4,5,6,7,8,9。每个桶中的值就是相同“位”上数字相同的元素个数。

下面以具体的例子演示一下基数排序的基本原理。
Demo:设有数据如下:
在这里插入图片描述
准备工作
1)创建10个桶。
2)求出最大的元素12608(也就是这里涉及到了比较大小,此外再无,童叟无欺),计算出其总位数是5,用于控制排序的总趟数。其实每一趟就是针对一个“位”上的元素进行排序。
3)为了方便求每一个元素不同“位”上的元素,设定一个基数,其取值分别是1,10,100,…,即第一趟排序时,值为1,用于求“个位”上的数字;第二趟排序时值为10,用于求“十位”上的数字,第三趟排序时,值为100,用于求“百位”上的数字,等等。
第一趟排序:
基数是1,根据“个位”数字将元素放入对应的桶里。
在这里插入图片描述
之后将根据“桶”号大小,将“桶”里元素按照桶号的大小顺序放入数组,得到如下结果:
在这里插入图片描述
第二趟排序:
基数是10,在第一趟排序的基础上,根据“十位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第三趟排序:
基数是100,在第二趟排序的基础上,根据“百位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第四趟排序:
基数是1000,在第三趟排序的基础上,根据“千位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
第五趟排序:
基数是10000,在第四趟排序的基础上,根据“万位”数字将元素放入对应的桶里。
在这里插入图片描述
之后再将“桶”里元素按照桶号的大小顺序放入数组,结果如下:
在这里插入图片描述
至此,基数排序结束。
在上述演示过程中,可以发现,在每一趟的排序中,每个“桶里”存放的元素个数都可能不同,如果直接把元素存放到桶里,就给算法的具体实现带来了很大麻烦。为了解决这个麻烦,“桶”里不再存储元素,而是改为存储元素的个数,同时引入一个新的数组作为缓存,存储每一趟的排序结果,之后再把排序结果存储到原数组中,这样就可以方便的实现桶排序了。
例如第一趟排序中的“桶”里数据就行可以修改为存放元素的个数,具体如下:
在这里插入图片描述
再做元素值的累加得到:
在这里插入图片描述
这样就可以得到原数组中每一个元素在排序后数组中的下标。有童鞋会突然发现有的桶里存放了多个元素,如何解决其位置呢?这个其实很简单,“桶”的元素值每用一次,就执行减1操作,就可以解决了。例如用bucket表示桶数组,bucket[4]中实际存放了两个元素,对应的原数组的元素分别是14和8844。bucket[4]的值是5,表示其中存放的元素在新数组的下标值是5,即当取走元素8844时,8844在新数组的下标是5,然后bucket[4]执行减1操作,其值就是4了,再取走元素14时,元素14在新数组的下标就是4了。

二、基数排序算法

记待排序的数组为data,元素个数为length。
**Step1:**创建桶bucket,有10个元素,初始值均为0,用于存放某“位”数字与桶元素编号相同的数组元素的个数;
求出数组的最大元素,同时算出其总位数count;
定义基数变量,例如base,其初始值为1;
分配缓存buff,其元素个数与原数组元素个数相同。
**Step 2:**数组的每个元素data[i]除以base之后对10求余数,记为k,执行bucket[k]++;转下一步;
**Step 3:**重新计算“桶”bucket中的每一个元素的值,让其等于前面元素之和,即
bucket[k+1] += bucket[k]
这样就可以计算出原数组 data中的每一个元素在新数组buff中的位置。(这一步是不是很神奇);转下一步;
**Step 4:**根据“桶”里的元素,也就是位置序号,将原数组data中的元素依次存放到buff中,再将buff中元素依次存入data中;转下一步;
**Step 5:**base *= 10,count–,判断count > 0, 如果为真,则转step2,否则结束排序。
三、基数排序的C程序
1. 基数排序代码

//利用桶排序算法对数组data进行从小到大排序 
void RadixSort( int data[], int length )
{
	int i, k, maxVal, count, base;
	int bucket[10] = { 0 };
	int *buff = new int[ length ];
	//求数组中的最大元素 
	maxVal = data[0];
	for( i = 1; i < length; i++ )
	{
		if( data[i] > maxVal )
		{
			maxVal = data[i];
		}
	}
	//printf( "maxVal = %d\n", maxVal );
	//计算最大元素的位数 
	count = 0;
	while( maxVal > 0 )
	{
		count++;
		maxVal /= 10;
	}
	//printf( "count = %d\n", count );
	base = 1;
	for( k = 1; k <= count; k++ )
	{
		//求每一个元素的 base位上的数字,对应的桶元素做累加 
		for( i = 0; i < length; i++  ) 
		{
			bucket[ ( data[i] / base ) % 10 ]++;
		}
		//重新计算桶元素的值,每一个元素都是其前面元素之和
		//如此操作,可以得到每一个桶中的元素在排序之后的位置下标 
		for( i = 1; i < 10; i++ ) 
		{
			bucket[i] += bucket[i-1];
		}
		//根据桶元素的值,将data中的元素重新排位存入buff中
		for( i = length-1; i >= 0; i-- ) 
		{
			buff[ bucket[ ( data[i] / base ) % 10 ] - 1 ] = data[i];
			bucket[ ( data[i] / base ) % 10 ]--;
		}
		//将buff中元素对位存放到data中
		for( i = 0; i < length; i++ ) 
		{
			data[i] = buff[i];
		}
		//清空桶,即让桶里元素归0 
		for( i = 0; i < 10; i++ ) 
		{
			bucket[i] = 0;
		}
		//base的值乘以10,之后进入下一趟排序 
		base *= 10;
	}
	delete[] buff;
}

2.完成测试代码

#include"stdio.h" 
void RadixSort( int data[], int length );

int main()
{
	int data[] = { 255, 12608, 31, 14, 21, 121, 268, 8844 };
	int length = 8;
	printf( " Initial Array    : " );
	for( int i = 0; i < length; i++ )
	{
		printf( "%8d", data[i] );
	}
	printf( "\n" );
	RadixSort( data, length );
	return 0;
}
//利用桶排序算法对数组data进行从小到大排序 
void RadixSort( int data[], int length )
{
	int i, k, maxVal, count, base;
	int bucket[10] = { 0 };
	int *buff = new int[ length ];
	//求数组中的最大元素 
	maxVal = data[0];
	for( i = 1; i < length; i++ )
	{
		if( data[i] > maxVal )
		{
			maxVal = data[i];
		}
	}
	//printf( "maxVal = %d\n", maxVal );
	//计算最大元素的位数 
	count = 0;
	while( maxVal > 0 )
	{
		count++;
		maxVal /= 10;
	}
	//printf( "count = %d\n", count );
	base = 1;
	for( k = 1; k <= count; k++ )
	{
		//求每一个元素的 base位上的数字,对应的桶元素做累加 
		for( i = 0; i < length; i++  ) 
		{
			bucket[ ( data[i] / base ) % 10 ]++;
		}
		//重新计算桶元素的值,每一个元素都是其前面元素之和
		//如此操作,可以得到每一个桶中的元素在排序之后的位置下标 
		for( i = 1; i < 10; i++ ) 
		{
			bucket[i] += bucket[i-1];
		}
		//根据桶元素的值,将data中的元素重新排位存入buff中
		for( i = length-1; i >= 0; i-- ) 
		{
			buff[ bucket[ ( data[i] / base ) % 10 ] - 1 ] = data[i];
			bucket[ ( data[i] / base ) % 10 ]--;
		}
		//将buff中元素对位存放到data中
		for( i = 0; i < length; i++ ) 
		{
			data[i] = buff[i];
		}
		//清空桶,即让桶里元素归0 
		for( i = 0; i < 10; i++ ) 
		{
			bucket[i] = 0;
		}
		//base的值乘以10,之后进入下一趟排序 
		base *= 10;
		
		//
		//向屏幕输出每一趟排序结果,便于观察 
		printf( " No. %d time sort : ", k );
		for( i = 0; i < length; i++ ) 
		{
			printf( "%8d", data[i] );
		}
		printf( "\n" );
		//
	}
	delete[] buff;
}

3.测试运行结果:
在这里插入图片描述

相关文章:

  • 生成模型的中Attention Mask说明
  • java毕业设计企业固定资产管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
  • Java---Java Web---JSP
  • opencv 机器学习-人脸识别
  • JavaScript的函数
  • java基于springboot+vue基本微信小程序的乒乓球课程管理系统 uniapp小程序
  • 安装数据库中间件——Mycat
  • 爬虫之Scrapy框架
  • 哈工大李治军老师操作系统笔记【23】:内存换出(Learning OS Concepts By Coding Them !)
  • Ubuntu 20.04 设置开机自启脚本
  • Vue2封装评论组件详细讲解
  • java-php-python-springboot校园新闻趣事计算机毕业设计
  • 使用Docker Compose搭建WordPress博客
  • 【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
  • 多任务学习(MTL)--学习笔记
  • [nginx文档翻译系列] 控制nginx
  • 4个实用的微服务测试策略
  • Angular4 模板式表单用法以及验证
  • HTML-表单
  • JAVA并发编程--1.基础概念
  • Spring框架之我见(三)——IOC、AOP
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • Vue小说阅读器(仿追书神器)
  • 离散点最小(凸)包围边界查找
  • 前端学习笔记之观察者模式
  • 设计模式走一遍---观察者模式
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 怎么把视频里的音乐提取出来
  • 自制字幕遮挡器
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​学习一下,什么是预包装食品?​
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (2015)JS ES6 必知的十个 特性
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (二开)Flink 修改源码拓展 SQL 语法
  • (九十四)函数和二维数组
  • (转)负载均衡,回话保持,cookie
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET 动态调用WebService + WSE + UsernameToken
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .NET框架设计—常被忽视的C#设计技巧
  • .NET使用存储过程实现对数据库的增删改查
  • .NET项目中存在多个web.config文件时的加载顺序
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • /var/log/cvslog 太大
  • []sim300 GPRS数据收发程序
  • [Asp.net MVC]Bundle合并,压缩js、css文件
  • [BZOJ4554][TJOI2016HEOI2016]游戏(匈牙利)