排序算法-----基数排序

news2024/11/14 21:42:36

 目录

前言

基数排序

算法思想

​编辑

算法示例

代码实现

1.队列queue.h 头文件

2.队列queue.c 源文件 

 3.主函数(radix_sort实现)

算法分析


前言

        今天我想把前面未更新完的排序算法补充一下,也就是基数排序的一种,这是跟计数排序一样类型的排序算法,是属于非比较型的排序算法,下面我们就一起来看看吧。

基数排序

        基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机 (Tabulation Machine)上的贡献。它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。 基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

算法思想

 上面这些理论思想看上去不太好理解,那下面我举个例子去简单解释一下。

算法示例

比如有这么一个数组 array = {53, 3, 542, 748, 14, 214, 154, 63, 616}  现在要去进行基数排序。

这里我们需要去创建一个长度为10的队列数组。Queue que[10]。然后这个队列数组作为根据基数储存数据的队列表。

这里,我们对数组中的每一个数字去进行遍历,然后进行按位求余,最先是个位数,得到的结果,根据对应的队列数组下标去进行入队操作。基数分配结果如下所示:

第一次基数分配(根据个位数分配)

que[0]----->NULL

que[1]----->NULL

que[2]----->542----->NULL

que[3]----->53------>3----->63----->NULL

que[4]----->14----->214----->154----->NULL

que[5]----->NULL

que[6]----->616----->NULL

que[7]----->NULL

que[8]----->748----->NULL

que[9]----->NULL

然后根据第一次基数计算出的结果,重新去排放这个数组,对这个队列表进行遍历(从上到下),然后出队操作,出队的结果放入到数组当中,第一次数组更新结果如下:

[ 542, 53, 3, 63, 14, 214, 154, 616, 748 ]

 第二次基数分配(根据十位数分配)

在进行第二次分配的时候,我们就根据上面已经跟新好了的数组去重新分配,这一次我们就要去进一位来分配。结果如下:

que[0]----->3----->NULL

que[1]----->14----->214----->616----->NULL

que[2]----->NULL

que[3]----->NULL

que[4]----->542----->748----->NULL

que[5]----->53----->154----->NULL

que[6]----->63----->NULL

que[7]----->NULL

que[8]----->NULL

que[9]----->NULL

根据第二次基数计算出的结果,重新去排放这个数组,对这个队列表进行遍历(从上到下),然后出队操作,出队的结果放入到数组当中,第二次数组更新结果如下: 

 [ 3, 14, 214 ,616, 542, 748, 53, 154, 63  ]

  第三次基数分配(根据百位数分配)

我们接着取上面第二次分配的数组结果,然后再次根据百位数求余数分配。结果如下:

que[0]----->3----->14----->53----->63----->NULL

que[1]----->154----->NULL

que[2]----->214----->NULL

que[3]----->NULL

que[4]----->NULL

que[5]----->542----->NULL

que[6]----->616----->NULL

que[7]----->748----->NULL

que[8]----->NULL

que[9]----->NULL

 根据第三次基数计算出的结果,重新去排放这个数组,对这个队列表进行遍历(从上到下),然后出队操作,出队的结果放入到数组当中,第三次数组更新结果如下:

 [ 3, 14, 53, 63, 154, 214, 542, 616, 748 ]

这里我们可以看出,已经拍好序了,但是我建议还是继续去第四次分配,直到全部的数字都在队列que[0]上。 

第四次基数分配(根据千位数分配)

同样的,这里我们把基数再进一位

que[0]----->3----->14----->53----->63----->154----->214----->542----->616----->748----->NULL

que[1]----->NULL

que[2]----->NULL

que[3]----->NULL

que[4]----->NULL

que[5]----->NULL

que[6]----->NULL

que[7]----->NULL

que[8]----->NULL

que[9]----->NULL

 此时的全部数字都在第一个队列上,这时候就完成了排序,只需要去对这个队列进行出队,然后把数据重新放入到数组当中,结果如下,至此,排序结束。

  [ 3, 14, 53, 63, 154, 214, 542, 616, 748 ]

 整体分配过程:

  • 假设r是排序码的基数,d是排序码的位数每位的类型是KeyType
  • 待排序的文件采用带表头结点的链表表示,类型为RadixList
  • 口为了便于实现记录的分配和收集,建立r个链表表示的队列,每个队列的类型为Queue

动态图:

代码实现

1.队列queue.h 头文件
#pragma once
#include<stdlib.h>

//数据类型
typedef int Datatype;

//节点
typedef struct node {
	Datatype data;
	struct node* next;
}Lnode;
//队列
typedef struct queue {
	int curnum;
	Lnode* front, * rear;
}Queue;

//队列初始化
void queue_init(Queue* que);
//判空
int isEmpty(Queue q);
//入队列
void enqueue(Queue *que, Datatype data);
//出队列
Lnode* dequeue(Queue* que);
//获取队头元素
Datatype get_headdata(Queue que);
2.队列queue.c 源文件 
#include"queue.h"
#include<assert.h>

//队列初始化
void queue_init(Queue* que) {
	que->curnum = 0;
	que->front = que->rear = NULL;
}

//创建一个节点
Lnode* create_node(Datatype data) {
	Lnode* node = (Lnode*)malloc(sizeof(Lnode));
	assert(node);
	node->data = data;
	node->next = NULL;
	return node;
}

//判空
int isEmpty(Queue q) {
	return q.curnum == 0;
}

//入队列
void enqueue(Queue *que,Datatype data) {
	Lnode* add = create_node(data);
	if (isEmpty(*que)) {
		que->front = que->rear = add;
		que->curnum++;
	}
	else {
		que->rear->next = add;
		que->rear = add;
		que->curnum++;
	}
}

//出队列
Lnode* dequeue(Queue *que) {
	if (isEmpty(*que))
		return NULL;
	if (que->curnum == 1)
		que->rear = NULL;
	Lnode* de = que->front;
	que->front = de->next;
	que->curnum--;
	return de;
}

//获取队头元素
Datatype get_headdata(Queue que) {
	return que.front->data;
}
 3.主函数(radix_sort实现)
#include<stdio.h>
#include<stdlib.h>
#include"queue.h"

void radix_sort(int* arr, int n) {
	Queue que[10];//创建下标为0~9的队列
	for (int i = 0; i < 10; i++) {
		queue_init(&que[i]);//初始化队列
	}
	for (int i = 0; i < n; i++) {
		enqueue(&que[arr[i] % 10], arr[i]);//把数组个位数的数字依次入队
	}
	int k = 0;
	int count = 10;
	while (que[0].curnum != n) {//如果数字里面全部的数据到第0个队列的时候就结束
		for (int i = 0; i < 10; i++) {
			while (!isEmpty(que[i])) {
				Lnode* pop = dequeue(&que[i]);//出队
				arr[k++] = pop->data;//重新放置数组
				//释放空间
				free(pop);
				pop = NULL;
			}
		}

		k = 0;
		
		for (int i = 0; i < n; i++) {
			//除以count取整,此时指向进一位数字,进行入队操作
			int x = arr[i] / count;
			enqueue(&que[x % 10], arr[i]);
		}
		count *= 10;//
	}
}

int main() {
	int array[] = { 123,0, 25,24,6,65,11,43,22,51 ,999 };
	printf("排序前:");
	for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
		printf("%d ", array[i]);
	}
	//基数排序
	radix_sort(array, sizeof(array) / sizeof(int));
	printf("\n排序后:");
	for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
		printf("%d ", array[i]);
	}
}

输出结果:

算法分析

  • 基数排序算法中,时间主要耗费在修改指针上一趟排序的时间为O(r+n),总共要进行d趟排序,基数排序的时间复杂度T(n) = O(d*(r+n))采用链表存储,排序时只修改链接指针,操作效率不受记录的信息量大小的影响
  • 排序中每个记录中增加了一个next字段(链表指针),还增加了一个queue 数组,故辅助空间S(n) = O(n+r)
  • 基数排序是稳定的

以上就是本期的全部内容了,我们下次见咯~

 分享一张壁纸:

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

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

相关文章

每日一题 1457. 二叉树中的伪回文路径(中等,DFS)

一句话&#xff0c;深度搜索所有路径&#xff0c;判断路径是否伪回文 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right clas…

leetCode 226.翻转二叉树

给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 示例 1: 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root…

C语言—一维数组

一、一维数组的创建 int arr1[10];char arr2[10];flout arr3[1];double arr4[20]; 数组创建,“[ ]”中要给一个常量&#xff0c;不能使用变量 二、一维数组初始化 int arr1[10]{1,2,3}; //不完全初始化int arr2[4]{3,4,5,6}; //完全初始化char arr3[4]{a,b,99,d};cha…

力扣日记11.25-【二叉树篇】对称二叉树

力扣日记&#xff1a;【二叉树篇】对称二叉树 日期&#xff1a;2023.11.25 参考&#xff1a;代码随想录、力扣 101. 对称二叉树 题目描述 难度&#xff1a;简单 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,…

【Docker】从零开始:11.Harbor搭建企业镜像仓库

【Docker】从零开始&#xff1a;11.Harbor搭建企业镜像仓库 1. Harbor介绍2. 软硬件要求(1). 硬件要求(2). 软件要求 3.Harbor优势4.Harbor的误区5.Harbor的几种安装方式6.在线安装(1).安装composer(2).配置内核参数,开启路由转发(3).下载安装包并解压(4).创建并修改配置文件(5…

基于51单片机的FM数字收音机系统电路设计

**单片机设计介绍&#xff0c;基于51单片机的FM数字收音机系统电路设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的FM数字收音机系统是一种用于接收和播放FM广播信号的设备&#xff0c;以下是一个基本的电路设…

TCP 三次握手和四次挥手(图解)

TCP 三次握手和四次挥手 建立连接-三次握手 什么是三次握手&#xff1f; 简单来说&#xff1a; 客户端向服务器发送 SYN 报文&#xff0c;请求建立连接。服务器收到 SYN 报文后&#xff0c;回复一个 SYNACK 的报文&#xff0c;表示同意建立连接。客户端收到 SYNACK 报文后&am…

数据结构—小堆的实现

前言&#xff1a;前面我们已经学习了二叉树&#xff0c;今天我们来学习堆&#xff0c;堆也是一个二叉树&#xff0c;堆有大堆有小堆&#xff0c;大堆父节点大于子节点&#xff0c;小堆父节点总小于子节点&#xff0c;我们在学习C语言的时候也有一个堆的概念&#xff0c;那个堆是…

MYSQL基础知识之【创建,删除,选择数据库】

文章目录 前言MySQL 创建数据库使用 mysqladmin 创建数据库使用 PHP脚本 创建数据库 MySQL 删除数据库使用 mysqladmin 删除数据库使用PHP脚本删除数据库 MySQL 选择数据库从命令提示窗口中选择MySQL数据库使用PHP脚本选择MySQL数据库 后言 前言 hello world欢迎来到前端的新世…

电路 buck-boost相关知识

BUCK-BOOST 文章目录 BUCK-BOOST前言一、DC-DC工作模式电容电感特性伏秒积平衡原理 二、BUCK电路三、BOOST电路四、BUCK-BOOST电路总结 前言 最近需要用到buck-boost相关的电路知识&#xff0c;于是便写下这篇文章复习一下。 一、DC-DC 在学习buck-boost电路之前我们先来看一…

python排序算法_归并排序

什么是归并排序&#xff1a; 归并排序是一种基于分治法的排序算法。它的基本思想是将待排序的序列分成若干个子序列&#xff0c;分别进行排序&#xff0c;然后再将已排序的子序列合并成一个有序的序列。 基本思想&#xff1a; 归并排序是用分治思想&#xff0c;分治模式在每一…

基于人工兔算法优化概率神经网络PNN的分类预测 - 附代码

基于人工兔算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于人工兔算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于人工兔优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

深入解析Selenium动作链:精通点击、拖拽、切换等操作

背景&#xff1a; 一些交互动作都是针对某个节点执行的。比如&#xff0c;对于输入框&#xff0c;我们就调用它的输入文字和清空文字方法&#xff1b;对于按钮&#xff0c;就调用它的点击方法。其实&#xff0c;还有另外一些操作&#xff0c;它们没有特定的执行对象&#xff0…

大模型微调技术

全量微调 部分参数微调 Adaper-Tuning 降维的意义 计算和存储成本去除冗余和噪声—特定任务训练数据有限减少模型复杂度避免过拟合风险适应任务需求过拟合 是指模型在训练数据上表现得很好,但在新的未见过的数据上表现较差的现象模型过于复杂,训练数据量不足等因素引起的 LO…

ubuntu22.04中ros2 安装rosbridge

ros2 启动rosbridge&#xff1a; 要启动ROS2中的rosbridge&#xff0c;需要先安装ROS2的rosbridge_suite软件包。使用以下命令安装&#xff1a; 更新过可忽略 sudo apt-get update安装命令 sudo apt-get install ros--rosbridge-suite 注意&#xff1a; 将替换为正在使用的R…

超实用:通过文字就可以操纵这款AI表格,不需要你懂Excel函数

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 工具介绍 今天给大家分享超实用的AI表格ChatExcel&#xff0c;这个工具是由北大团队在2022年3月开始开发的AI表格处理神器&#xff0c;上传你的表格后&#xff0c;只需要用文字描述你…

NX二次开发UF_CURVE_ask_int_curves 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_int_curves Defined in: uf_curve.h int UF_CURVE_ask_int_curves(tag_t int_curve_object, int * num_curves, tag_t * * intersection_curves ) overview 概述 Ret…

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码

基于厨师算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于厨师算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于厨师优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

leetCode 226.翻转二叉树 递归 + 非递归 + 前中后序 + 层序遍历 【深度和广度优先遍历】

我的往期文章&#xff1a; leetCode 226.翻转二叉树-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134613347?spm1001.2014.3001.5501 &#xff08;一&#xff09;递归做法&#xff08;深度&#xff09; C代码&#xff1a;前序遍历 class Solution { pu…

电力感知边缘计算网关产品设计方案-网关软件设计方案

网关采用网络协议和软件技术在通信网络中针对工业协议、互联网通用协议进行分析和记录,提升工业控制系统环境的安全防护能力。A类和B类网关采用容器技术的软件架构,采用C/S架构软件客户端提供应用软件平台,为管理员提供功能丰富的图形管理控制界面。 因A类和B类网关在产品定…