计数排序 (Counting Sort)_20230709

news2024/11/25 9:00:27

计数排序(Counting Sort)

  1. 前言

计数排序的对象一般为分布在[0-k]范围内的非负整数,计数器类似哈希函数的线性映射,它确定了数值本身和它在序列中的总数量之间的基本关系。它的本质是计算某个数在临时序列中(原序列大小相同,但下标从1开始)的位置,在后续的映射中,直接把某个位置上放置相关的数值。

  1. 计数排序过程

计数排序中至少涉及到三个序列(数组),为了叙述方便,定义待排序数组为A,排序数组为B, 计数数组为C。给定输入数组A[0…n-1],数组的大小为n, 在数组中最大的非负值为k。排序完成的后,数组B[1…n]中的元素为有序序列,值得一提的是,这里B的下标必须从1开始,因为它的下标与计数数组相关,由于A为非空数组,其包含元素数量至少为1。

计数数组的下标为[0…k],下标的值来自于A[0…n-1],通过对待排序数组中元素的数量进行统计,从而形成一个计数数组。

形成计数数组后,我们需要对计数的值进行累计处理,也就是需要找到小于它的值数量,也即当前值在B[1…n]数组中所处的位置。给点某个非重复的数字x(distinct),假定有10个数字小于等于x,那么x在B[1…n]数组中所处的位置为11。

计数排序需要处理序列元素重复的情形,假定序列中包含3个数字x,此时就需要对count[x](累计)进行操作,第一次插入至B[1…n]完成后,count[x]的值就应该自减1,确保第二次排序的时候,所获得count[x]为正确的值,依次直至此处的count[x]自减3为止。

  1. 计数排序过程演示

为了更好演示计数排序过程,具体看一个例子。给定待排序的数组A,其中包含8个元素,最大元素的值为5,可以取k值为5或6,也就是计数数组大小设定为6或7。通过对待排序元素进行线性扫描,计算出每个元素的总计数。此时数组C中下标值代表A数组中的元素值[0…k],数组C中的元素值对应A中等于当前C的下标值的计数值。比如下标为3的C[3]=3代表的含义为原数组中含有3个3。

在这里插入图片描述

对于数组C,目前代表的仅是每个下标值对应的计数数量,要找到其在临时数组B中的插入位置,需要查找每个下标值在B中的位置,那么就需要进行累加求和,也就是计算小于某个下标值(对应A中的值)前面的总的计数和,这样就可以找到在数组B中的插入位置。依照此思路,对数组C进行累加操作,更新C数组值,代表累加值。

在这里插入图片描述

为了保证计数排序的原位(in-place)特性,需要从后向前对数组A中的元素进行遍历,具体看一个例子。首先检查A[7]的值,由于A[7]等于3,然后寻找下标为3的C数组对应的值,C[3]的当前值对应的值就是A[7]应该在数组B中应该放置的位置,也即B[7]=3。考虑到A中的存在重复元素的可能性,所以当[7]位置放置3后,需要对C[3]自减1,以便正确放置后续重复元素,具体过程对应为:

在这里插入图片描述

为了更加深入理解过程,再对A[6]进行相关操作演示。通过观察A[6]对应的值为0,而C[0]的值对应A[6]值放置的位置,所以B[2]=A[6],同上分析,对C[0]当前的值进行自减,C[0]由2减少至1。

在这里插入图片描述

通过不断的向前遍历,最终我们得到B数组的值为,

在这里插入图片描述

细心的读者可能以及发现,B数组对应的下标从1开始,它包含的元素数量和A数组相同,也即n下标结束。

  1. 代码实现

取决于数据对象的不同,计数排序可归为两种方式,第一类方式假定数据对象不仅包含键值,而且每个键值伴随着其它不同的相关信息值;第二类方式假定操作对象仅为非负整数,这个过程可以省略数组B,从而使操作过程所占空间优化。

4.1 第一类方式

头函数counting_sort.h

/**
 * @file counting_sort.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-06-29
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef COUNTING_SORT_H
#define COUNTING_SORT_H
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/**
 * @brief Find the k value that makes sure value in arr array falls into [0...k]
 * 
 * @param arr Array 
 * @param n  Number of element in the array
 * @return int -Return k value
 */
int  find_k_value(int *arr, int n);

/**
 * @brief Use addtional array temp[] to realize the counting sort function
 * 
 * @param arr Array object
 * @param n Number of element in the array
 */
void counting_sort(int *arr,int n);

/**
 * @brief Display the element in the array
 * 
 * @param arr Array object
 * @param n Number of element in the array
 */
void display_list(int *arr, int n);

#endif

函数实现

/**
 * @file counting_sort.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-06-29
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef COUNTING_SORT_C
#define COUNTING_SORT_C
#include "counting_sort.h"

void counting_sort(int *arr, int n)
{
   // Use count[K] to keep record of the number of element
   int k;
   int i;
   int j;
   k = find_k_value(arr, n);
   int count[k];
   int temp[n+1];

   memset(count,0,sizeof(int)*k);

   for(i=0;i<n;i++) //start from the index 0;
   {
        count[arr[i]]++;
   }

   for(j=1;j<k;j++) //start from j=1 instead of j=0
   {
        count[j]=count[j]+count[j-1];
   }

   for(i=n-1;i>=0;i--)
   {
        temp[count[arr[i]]]=arr[i];
        count[arr[i]]--;
   }

   memcpy(arr,temp+1,sizeof(int)*n);

   return;

}

int find_k_value(int *arr, int n)
{
    int max_value=-1;
    int i;

    for(i=0;i<n;i++)
    {
        if(max_value< arr[i])
        {
            max_value=arr[i];
        }
    }

    return max_value+1;
}

void display_list(int *arr, int n)
{
    int i;

    for(i=0;i<n;i++)
    {
        printf("%d ",arr[i]);
    }
    
    printf("\n");
}

#endif

测试函数

/**
 * @file counting_sort_main.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-06-17
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef COUNTING_SORT_MAIN_C
#define COUNTING_SORT_MAIN_C
#include "counting_sort.c"

int main(void)
{
    int arr[] = {2, 8, 3, 0, 2, 3, 8, 3};
    int n = sizeof(arr) / sizeof(int);

    printf("The number is listed before sorting:\n");
    display_list(arr, n);

    counting_sort(arr,n);

    printf("The number is listed after sorting:\n");
    display_list(arr, n);

    getchar();

    return EXIT_SUCCESS;
}


#endif

4.2 第二类方式

为了方便读者阅读,仅对主函数进行展现,

void counting_sort(int *arr, int n)
{
    int         i;
    int         j;
    int         k;
    int         m = find_k_value(arr, n);
    int         count[m];

    memset(count,0,sizeof(int)*m);

    for(i=0;i<n;i++)
    {
        count[arr[i]]++;
    }

    for(j=0,k=0;j<m;j++)
    {
        while(count[j]) //lean code
        {
            arr[k]=j;
            k++;
            count[j]--;
        }
    }
}
  1. 算法复杂度

计数算法的复杂度可以表示为O(2n+k),n表示待排序数组中元素数量,k表示原始数组中的最大值。因为整体涉及到两个n的循环,所以为2n。由于k=O(n),所以最终算法的时间复杂度为O(n)。

  1. 小结

对于本算法,其难点在于理解C数组中的累加,同时为了保证原位排序,需要对原数组进行倒序遍历,确保first in first 和after in after的原则。对于第二类算法,由于其对象均为非负整数,充分利用C数组的特性,直接赋值给到A数组,从而省略过渡数组B。

参考文献:

a. 《Introduction to algorithm 4th edition》

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

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

相关文章

零售业未来如何破局?抓住数智化经营的两把利刃!

导语 | 数字化转型浪潮席卷了千行百业&#xff0c;有人从中看出了汹涌的挑战&#xff0c;也有人从中嗅出了美妙的商机。对于零售企业而言&#xff0c;当前数智经营进入了哪个阶段&#xff1f;未来的破局之道又在何方&#xff1f;我们邀请到了广东省 CIO 协会消费品与零售行业分…

API接口知识小结(电商API接入)

应用程序接口API&#xff08;Application Programming Interface&#xff09;&#xff0c;是提供特定业务输出能力、连接不同系统的一种约定。这里包括外部系统与提供服务的系统&#xff08;中后台系统&#xff09;或后台不同系统之间的交互点。包括外部接口、内部接口&#xf…

Redis专题学习(一)Redis核心数据结构实战与高性能原理剖析

redis是key-value的存储格式&#xff0c; key是string类型的&#xff0c; value可以有五种基本的数据结构&#xff1a;string、hash、list、set、zset 来看看 这5中基本数据类型的基本使用和应用 一.字符串string string是最常见和最基本的数据结构 基本使用&#xff1a; …

leetcode 501. 二叉搜索树中的众数

2023.7.10 这道题我的思路是适用于任意二叉树的思路&#xff1a; 先用任意一个遍历方法将节点保存至map<int,int>中&#xff0c;key为节点值&#xff0c;value为频率。由于map没有对value&#xff08;频率&#xff09;排序的方法&#xff0c;所以将map的键值对转移至vec…

TCP协议三次握手的抓包模拟

三次握手(Three-way Handshake)&#xff0c;是指建立一个 TCP 连接时&#xff0c;需要客户端和服务器总共发送3个包。 第一次握手([SYN], Seq x) 客户端发送一个SYN标记的包&#xff0c;Seq初始序列号x&#xff0c;发送完成后客户端进入SYN_SEND状态。 第二次握手([SYN,ACK]…

一个面试知识点: CreateThread() 与 _beginthread() 的区别

如题: 像现在有c11, 加上不写原生winApi 很久没有用这俩玩意了, 真的忘记了, 被问到这个的时候, 我还清晰记得之前在WPS里的时候专门了解这个 CreateThread的简单用法贴下面了 c win32API 【CreateThread】创建线程 其实就简单的记住 1._beginthread 底层还是 调用的 Creat…

calltree的安装与使用

目录 0 calltree 和 graphviz的关系 1 graphviz的安装很简单 : 2 有难的是calltree的安装,需要改一下代码. Doxygen的使用 参考 这个人的笔记都挺好的, 没事多看看 0 calltree 和 graphviz的关系 要想绘制函数调用图的话,需要用到2个工具, calltree 和 graphviz. calltr…

【记录】Yolov5官网下载避坑记录

写在前面&#xff1a;刚开始接触Yolov5时&#xff0c;对一些基础的概念很模糊&#xff0c;在官网下载也不知道该下载什么版本好。后续更是遇到了一些奇奇怪怪的坑。在此记录一下最初的研究过程&#xff0c;顺带填一下yolov5避坑专栏后面的坑。 目录 一、Yolov5误区 二、官网…

UVM中sequence机制-数据产生及传递机制

一 基础知识 参考 UVM——sequence机制(数据激励的产生、配置方式)_uvm激励_SD.ZHAI的博客-CSDN博客https://blog.csdn.net/weixin_46022434/article/details/105600081 1.1 sequence执行流程 1.2 sequence的启动方式 1.3 sequence数据产生--body()

TortoiseGit 入门指南03:将修改提交到版本库

你现在应该已经有了一个仓库&#xff0c;在工作过程中会对项目做一些修改&#xff0c;比如添加代码、修复错误等等&#xff0c;你将不定时的将这些更改 提交&#xff08;commit&#xff09;到代码仓库。 术语 提交 是将 暂存区 内容放入 版本库 。这个过程涉及到 Git 的一些基…

如何对你的代码进行内存消耗分析

对象生命周期 下面两种创建对象的语句有什么不同呢? 对于 Object myObject;,该对象被创建在栈上,它的特点就是脱离作用域后会自动销毁。而对于 new Object(),它会在堆上动态创建一个对象,它的特点就是即使脱离作用域,该对象也会一直存在,除非你手动释放(delete)它,否…

Layui 简单介绍及入门

目录 一.Layui &#xff08;国产品牌&#xff09; 1.1 Layui是什么 二.比较layui和easyui&#xff0c;bootstrap的区别 2.1 layui和bootstrap的对比 2.2 layui和easyui对比 三.Layui入门 四.案例 一.Layui &#xff08;国产品牌&#xff09; 1.1 Layui是什么 用我的话来…

峟思科普:水库坝体的裂缝防治措施有哪些

水库大坝的建设与施工是一项庞大的系统工程&#xff0c;它包括设计、施工、监理等各个环节。而裂缝问题是整个工程中最常见的问题之一&#xff0c;大坝工程中出现裂缝是一件很严重的事&#xff0c;不仅影响大坝的使用寿命&#xff0c;而且严重影响水库工程的整体效益。坝体裂缝…

让GPT来聊聊目前软件测试行业的就业形式

最近两个月一直处于忙碌状态&#xff1a;跳槽、转行、学习新的编程语言&#xff08;Python Golang&#xff09;、赶工期、面试招人……也正是这一些列的经历&#xff0c;对目前的就业形势和软件从业人员的发展有了一些新的观察和思考&#xff0c;在这篇文章分享给大家。 整体…

亚马逊云科技143项安全标准与合规性认证,帮助企业满足安全合规要求

在亚马逊云科技&#xff0c;为满足客户不断变化的需求&#xff0c;亚马逊云科技持续创新与迭代&#xff0c;设计的服务能帮助客户满足最严格的安全和合规性要求。针对安全相关工作&#xff0c;亚马逊云科技服务团队与Amazon Security Guardians云守护者项目密切配合&#xff0c…

Jira Tempo :如何创建 Dynamic Dropdown 类型的 Work Attributes?

官方示例&#xff1a; Jira Tempo 可以创建以下类型的工作属性&#xff08;Work Attributes&#xff09;: 其中&#xff0c;Dynamic Dropdown 支持外部接口&#xff0c;可以自己提供一个接口连接&#xff0c;如下&#xff1a; 官方示例中&#xff0c;给了一个 php 版本的示例…

UVM学习笔记--寄存器模型 Register Model

1.寄存器模型( Register model )简介 UVM的寄存器模型是一组高级抽象的类&#xff0c;用来对DUT中具有地址映射的寄存器和存储器进行建模。它非常贴切的反映DUT中寄存器的各种特性&#xff0c;可以产生激励作用于DUT并进行寄存器功能检查。通过UVM的寄存器模型&#xf…

通信算法之176: 基于Matlab的OFDM通信系统关键基带算法设计6-流程

一. 接收算法流程 粗同步&#xff08;分组检测&#xff09; 载波同步&#xff08;精细频偏估计&#xff09; 精同步&#xff08;OFDM起始&#xff0c;符号同步&#xff09; 1.4 信道估计&#xff08;长序列&#xff09; 1.5 信道均衡&#xff08;所有数据OFDM符号&#xff…

(黑客)自学路线

一、什么是网络安全&#xff08;黑客&#xff09; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领…

基于SpringBoot+vue的准妈妈孕期交流平台设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…