图解并用 C 语言实现非比较排序(计数排序、桶排序和基数排序)

目录

一、计数排序

二、桶排序

三、基数排序



一、计数排序

算法步骤

  1. 找出待排序数组 arr 中的最小值和最大值(分别用 min 和 max 表示)。

  2. 创建一个长度为 max - min + 1、元素初始值全为 0 的计数器数组 count

  3. 扫描一遍原始数组,将 arr[i] - min 作为下标,并将该下标的计数器增 1。

  4. 扫描一遍计数器数组,按顺序把值收集起来。

void CountSort(int* arr, int n)
{
    // 找出待排序数组中的最小值和最大值
    int min = arr[0];
    int max = arr[0];
    for (int i = 1; i < n; ++i)
    {
        if (arr[i] < min)
        {
            min = arr[i];
        }
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }
    // 创建一个长度为 max - min + 1,元素初始值全为 0 的计数器数组
    int* count = (int*)calloc(max - min + 1, sizeof(int));
    if (NULL == count)
    {
        perror("malloc failed!");
        return;
    }
    // 扫描原始数组
    for (int i = 0; i < n; ++i)
    {
        count[arr[i] - min]++;
    }
    // 扫描计数器数组
    int k = 0;
    for (int i = 0; i < max - min + 1; ++i)
    {
        for (int j = 0; j < count[i]; ++j)
        {
            arr[k++] = i + min;
        }
    }
}

计数排序适合范围集中,且范围不大的整型数组


二、桶排序

桶排序(Bucket Sort)或所谓的箱排序,其工作原理是:假设输入数据服从均匀分布,将数据分到有限数量的桶里,然后对每个桶分别排序,最后把桶的数据合并。

桶排序的时间复杂度,取决于各个桶之间数据进行排序的时间复杂度,因为其他部分的时间复杂度都为 O(n)。很显然,桶划分地越小,各个桶之间的数据越少,排序所用的时间也会越少,但相应的空间消耗会增加。

void BucketSort(int* arr, int n)
{
    int bucket[5][5] = { 0 };  // 分配 5 个桶,每个桶最多放 5 个元素
    int bucketCount[5] = { 0 };  // 这 5 个桶的计数器的计数器
    // 将数据放入桶中
    for (int i = 0; i < n; ++i)
    {
        bucket[arr[i] / 10][bucketCount[arr[i] / 10]++] = arr[i];
    }
    // 对每个桶进行排序
    for (int i = 0; i < 5; ++i)
    {
        QuickSort(bucket[i], bucketCount[i]);
    }
    // 把每个桶中的数据填充到数组中
    int k = 0;
    for (int i = 0; i < 5; ++i)
    {
        for (int j = 0; j < bucketCount[i]; ++j)
        {
            arr[k++] = bucket[i][j];
        }
    }
}

在现实世界中,大部分的数据分布是均匀的,或者在设计的时候可以让它均匀分布,或者说可以转换为均匀地分布。当数据均匀地分布,桶排序的效率就能发挥出来。

理解桶思想可以设计出高效的算法。


三、基数排序

基数排序(Radix Sort)是一种借助多关键排序的思想对单逻辑关键字进行排序的方法。

void DistrAndCollect(int* arr, int n, int exp)
{
    Queue q[10];
    for (int i = 0; i < 10; ++i)
    {
        QueueInit(&q[i]);
    }
    // 分配
    for (int i = 0; i < n; ++i)
    {
        int j = (arr[i] / exp) % 10;
        QueuePush(&q[j], arr[i]);  // 将 arr[i] 分配到下标为 j 的队列(桶)中
    }
    // 收集
    int j = 0;
    for (int i = 0; i < 10; ++i)
    {
        while (!QueueEmpty(&q[i]))
        {
            arr[j++] = QueueFront(&q[i]);
            QueuePop(&q[i]);
        }
    }
}
​
void RadixSort(int* arr, int n)
{
    // 找出待排序数组中的最大值
    int max = arr[0];
    for (int i = 1; i < n; ++i)
    {
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }
    // 分配和收集
    for (int exp = 1; max / exp > 0; exp *= 10)
    {
        // exp 表示排序指数,
        // 当 exp 为 1 时,表示按个位分配,
        // 当 exp 为 10 时,表示按十位分配,依次类推。
        DistrAndCollect(arr, n, exp);
    }
}

 

void DistrAndCollect(int* arr, int n, int exp)
{
    int bucket[10] = { 0 };  // 初始化 10 个桶
    int* result = (int*)malloc(sizeof(int) * n);
    if (NULL == result)
    {
        perror("malloc failed!");
        return;
    }
    // 分配
    for (int i = 0; i < n; ++i)
    {
        bucket[(arr[i] / exp) % 10]++;
    }
    for (int i = 1; i < 10; ++i)
    {
        bucket[i] = bucket[i] + bucket[i - 1];
    }
    // 收集
    for (int i = n - 1; i >= 0; --i)  // 从后向前遍历数组 arr
    {
        int j = (arr[i] / exp) % 10;
        result[bucket[j] - 1] = arr[i];
        bucket[j]--;
    }
    memmove(arr, result, sizeof(int) * n);
}
​
void RadixSort(int* arr, int n)
{
    // 找出待排序数组中的最大值
    int max = arr[0];
    for (int i = 1; i < n; ++i)
    {
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }
    // 分配和收集
    for (int exp = 1; max / exp > 0; exp *= 10)
    {
        // exp 表示分配指数,
        // 当 exp 为 1 时,表示按个位分配,
        // 当 exp 为 10 时,表示按十位分配,依次类推。
        DistrAndCollect(arr, n, exp);
    }
}

基数排序的其他应用,例如超女选秀活动,如果要把超女的出生日期排序:

  1. 年:1990 ~ 2005(15 个桶)

  2. 月:1 ~ 12(12 个桶)

  3. 日:1 ~ 31(31 个桶)

创作不易,可以点点赞,然后关注一下博主~ 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023 年嵌入式世界的3 大趋势分析

目录 大家好&#xff0c;本文讲解了嵌入式发展的3个大趋势&#xff0c;分享给大家。 趋势#1 – Visual Studio Code Integration 趋势#2 –支持“现代”软件流程 趋势 #3 – 在设计中利用 AI 和 ML 结论 大家好&#xff0c;本文讲解了嵌入式发展的3个大趋势&#xff0c;分享…

Python圈的普罗米修斯——一套近乎完善的监控系统

文章目录前言一、怎么采集监控数据&#xff1f;二、采集的数据结构与指标类型2.1 数据结构2.2 指标类型2.3 实例概念2.4.数据可视化2.5.应用前景总结前言 普罗米修斯(Prometheus)是一个SoundCloud公司开源的监控系统。当年&#xff0c;由于SoundCloud公司生产了太多的服务&…

网络安全实战之植入后门程序

在 VMware 上建立两个虚拟机&#xff1a;win7 和 kali。 Kali&#xff1a;它是 Linux 发行版的操作系统&#xff0c;它拥有超过 300 个渗透测试工具&#xff0c;就不用自己再去找安装包&#xff0c;去安装到我们自己的电脑上了&#xff0c;毕竟自己从网上找到&#xff0c;也不…

如何把数据库中的数据显示到页面

主要内容&#xff1a;使用JDBC访问数据库中数据&#xff08;Java Web数据可视化案例&#xff09; 文章目录前期准备&#xff1a;案例&#xff1a;第一步&#xff1a;创建数据库及数据第二步&#xff1a;编写实体类第三步&#xff1a;编写Dao类第四步&#xff1a;编写Servlet代码…

springboot集成hadoop3.2.4HDFS

前言 记录springboot集成hadoop3.2.4版本&#xff0c;并且调用HDFS的相关接口&#xff0c;这里就不展示springboot工程的建立了&#xff0c;这个你们自己去建工程很多教程。 一、springboot配置文件修改 1.1 pom文件修改 <!-- hadoop依赖 --><dependency><gro…

Stable Diffusion - API和微服务开发

Stable Diffusion 是一种尖端的开源工具&#xff0c;用于从文本生成图像。 Stable Diffusion Web UI 通过 API 和交互式 UI 打开了许多这些功能。 我们将首先介绍如何使用此 API&#xff0c;然后设置一个示例&#xff0c;将其用作隐私保护微服务以从图像中删除人物。 推荐&…

一种轻量的“虚拟机”——Windows 沙盒模式

Windows 沙盒模式Windows沙盒的好处操作步骤Windows沙盒的好处 相比虚拟机和第三方的沙盒软件&#xff0c;Windows Sandbox启用后仅占用100MB硬盘空间&#xff0c;还能与物理机安全地共享部分内存空间。简单来说就是易用、免费、不卡机&#xff01; 由于要保证沙盒内的数据不…

(九)【软件设计师】计算机系统-浮点数习题

文章目录一、2009年下半年第3、4题二、2011年上半年第5题三、2012年下半年第3题四、2015年上半年第1题五、2015年下半年第3题六、2016年下半年第3题七、2018年上半年第1题八、2020年下半年第3题知识点回顾 &#xff08;八&#xff09;【软件设计师】计算机系统—浮点数一、2009…

Android13 PMS是如何启动的?

作者&#xff1a;Arthas0v0 平常使用安卓实际就是在使用各种app&#xff0c;而下载的app实际是一个apk文件。这个apk文件的安装就交给了PackageManagerService来实现。PackageManagerService的启动也是在SystemServer中。这个过程比较长需要长一点的时间来理。 SystemServer.s…

ORACLE EBS 系统架构与应用实践(一)

一、从ERP到EBS 从上世纪70年代晚期的物料需求计划MRP&#xff08;Material Requirements Planning&#xff09;到80年代的MRP II&#xff0c;再到90年代的企业资源计划ERP&#xff08;Enterprise Resource Planning&#xff09;&#xff0c;企业管理软件&#xff08;或曰应用…

u盘里的文件被自动删除了怎么办?五种数据恢复方案

u盘是我们日常生活中常常用到的一种便携式存储设备&#xff0c;可以帮助我们存储和携带大量的文件信息。但是&#xff0c;使用过程中难免会遇到一些问题&#xff0c;例如u盘会自己删除文件的情况&#xff0c;如果你遇到了这种情况&#xff0c;该怎样找回u盘自己删除的文件呢&am…

AI 芯片的简要发展历史

随着人工智能领域不断取得突破性进展。作为实现人工智能技术的重要基石&#xff0c;AI芯片拥有巨大的产业价值和战略地位。作为人工智能产业链的关键环节和硬件基础&#xff0c;AI芯片有着极高的技术研发和创新的壁垒。从芯片发展的趋势来看&#xff0c;现在仍处于AI芯片发展的…

FFMPEG: [ API ] >打开/关闭一个输入文件

它们是成对出现的. ffmpeg 把输入文件--转换成--->AVFormatContext实例 ◆ avformat_open_input() int avformat_open_input(AVFormatContext ** ps,const char * url,ff_const59 AVInputFormat * fmt,AVDictionary ** options )Open an input stream and read the header.…

分子生物学 第二章 遗传物质

文章目录第二章 遗传物质第一节 遗传物质的分子本质大多数生物体的遗传物质是DNA有些生物体的遗传物质是RNA蛋白质能否充当遗传物质第二节 核酸的结构1 DNA双螺旋结构的特征2 影响DNA双螺旋结构稳定性的因素3 DNA结构的多态性4 DNA多链结构5 DNA的超螺旋结构6 RNA的二级结构第三…

归并排序(非递归实现) 计数排序

上一期我们说了归并排序的递归是如何实现的&#xff0c;但是递归如果层次太多的话容易栈溢出&#xff0c;所以我们还需要掌握非递归的实现&#xff0c;但是我们非递归需要如何实现&#xff1f; 下面我们就来看一下非递归的实现 归并排序的非递归实现他并不需要栈队列这些东西…

day10_oop

今日内容 零、 复习昨日 一、面向对象的概念 二、面向对象编程 三、内存图 零、 复习昨日 晨考复习… 一、作业 package com.qf.homework;import java.util.Arrays;/*** --- 天道酬勤 ---** author QiuShiju* desc* ----------------* 引用数据类型的默认初始值null*/ public …

Redis数据备份与恢复

Redis数据备份与恢复 文章目录Redis数据备份与恢复1. Redis备份的方式2. RDB持久化2.1 什么是RDB&#xff1f;2.2 Fork操作2.3 save VS bgsave2.4 关于RDB备份的一些配置项2.5 RDB的备份与恢复2.6 RDB的自动触发2.7 RDB的优势与劣势3. AOF持久化3.1 什么是AOF&#xff1f;3.2 A…

ASP一个简单的网上教务系统模型的设计与实现

对于一个学校来说&#xff0c;大量教师信息&#xff0c;学生信息管理&#xff0c;学生成绩管理&#xff0c;基本数据的维护都难于通过传统的方法进行管理&#xff1a;这就迫切需要利用计算机技术来帮助学校管理者处理这些日常管理。本系统正是为了简化教学任务的管理&#xff0…

Python爬虫之多线程加快爬取速度

之前我们学习了动态翻页我们实现了网页的动态的分页&#xff0c;此时我们可以爬取所有的公开信息了&#xff0c;经过几十个小时的不懈努力&#xff0c;一共获取了 16万 条数据&#xff0c;但是软件的效率实在是有点低了&#xff0c;看了下获取 10 万条数据的时间超过了 56 个小…

Vue——组件基础

目录 定义一个组件​ 使用组件​ 传递 props​ 监听事件​ 通过插槽来分配内容​ 动态组件​ DOM 模板解析注意事项​ 大小写区分​ 闭合标签​ 元素位置限制​ 组件允许我们将 UI 划分为独立的、可重用的部分&#xff0c;并且可以对每个部分进行单独的思考。在实际应…