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

【初阶数据结构】计数排序 :感受非比较排序的魅力

文章目录

  • 前言
  • 1. 什么是计数排序?
  • 2. 计数排序的算法思路
    • 2.1 绝对位置和相对位置
    • 2.2 根据计数数组的信息来确认
  • 3. 计数排序的代码
  • 4. 算法分析
  • 5. 计数排序的优缺点
  • 6.计数排序的应用场景

前言

如果大家仔细思考的话,可能会发现这么一个问题。我们学的七大排序(冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序)都是通过数组中元素之间比较进行数组中数字挪动,从而达到排序的目的。

以上的排序我们也把它们称为"比较排序"。那在本文中,我们就了解一个非比较的排序——“计数排序”。

hahaha

1. 什么是计数排序?

计数排序(Counting Sort)是一种线性时间复杂度的排序算法,它通过统计数组中元素的出现次数,来确定每个元素在排序后的数组中的正确位置。

为了让大家更好的理解理解计数排序,我给大家画一幅图:
计数排序的流程图

2. 计数排序的算法思路

  1. 🍉统计相同元素出现的次数
  2. 🍉根据统计的结果将序列回收到原来的序列中

想必上面的图已经给你一点提示了。这里就会有两个问题想问一下大家:

  1. 怎么将待排序的数组中的数字映射到计数的数组中?
  2. 如何将计数数组中的元素回写到待待排序的数组中,从而达到排序的效果?

2.1 绝对位置和相对位置

绝对位置:从数组首元素开始计算,剩余每个元素的位置都是按照数组首元素为参照的。

举个例子:数组a:[1,5,6,8,9,7,3,2,0,4],那对于计数数组来说用绝对位置就比较好,原因是这个待排序的数组a的元素最小值是为0,最大值为9,这里用绝对位置就比较舒服!

相对位置:先选取待排序数组中的最小值,以这个最小值为基准,从而给剩余的元素在计数数组中确定位置。

举个例子:数组a:[101,100,106,103,105,104],如果你这里硬是要使用绝对位置的话,你就要申请106个整型数据的空间,而我只有6个数据需要排序,开这么大的数据空间就有点得不偿失了。所以我们得采用相对位置,以数组a中的最小值(100)为基准,其余元素按照大小关系依次记录到计数数组中。

所以我建议大家使用相对位置来实现计数排序。

2.2 根据计数数组的信息来确认

我们创建了计数数组之后,我们要怎么讲信息还原到原序列中呢?

就根据我们设定的相对位置加上原本的最小值,就可以还原出待排序数组中元素的值。然后再建立一个循环,根据count数组中的位置元素的值决定循环这个相对位置加上原本的最小值多少次。

3. 计数排序的代码

#include<stdio.h>
#include<stdlib.h>

//计数排序 -- 是一个非比较的排序方式
//通过统计数组中每个数字出现的次数,通过创建一个count数组记录这些数字对应出现的次数。(利用相对位置进行对应)

void CountSort(int* a, int n)
{
	
	//1.为获得相对位置,我们要想找到数组中的最小值还要找最大值
	int min = a[0],max = a[0];
	for (int i = 1; i < n; i++)
	{
		if (min > a[i])
		{
			min = a[i];
		}

		if (max < a[i])
		{
			max = a[i];
		}
	}

	int* count = (int*)calloc(max, sizeof(int));
	if (count == NULL)
	{
		perror("calloc fail");
		return;
	}


	//统计数字出现的次数,并将其对应到count数组中
	for (int i = 0; i < n; i++)
	{
		//a[i]-min:就相当于a[i]其在count数组中的相对位置
		count[a[i] - min]++;
	}

	int j = 0;
	//根据count数组复现数字到目标数组中
	for (int i = 0; i < max; i++)
	{
		while (count[i]--)
		{
			a[j++] = min + i;
		}
	}
}

int main()
{
	int a[] = { 1,1,6,6,9,8,4,5,1,3,4,7,4 };
	int size = sizeof(a) / sizeof(int);

	CountSort(a,size);

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

这个排序比较简单,大家只要注重算法的思路就行!!!

4. 算法分析

时间复杂度:计数排序的时间复杂度为 O ( n + k ) O(n + k) O(n+k),其中 n n n 是输入数组的大小, k k k 是输入数据的范围大小。由于不涉及元素之间的比较,计数排序可以在较小的数据范围内达到比比较类排序更高效的结果。

空间复杂度:额外的空间复杂度为 O ( k ) O(k) O(k),因为需要创建一个计数数组用来记录元素的出现次数和累积结果。如果 k k k 过大,则计数排序的空间消耗会很大。

稳定性:计数排序是一种稳定的排序算法,即排序后相同的元素相对位置不发生改变。这一点对于一些带有附加信息的数据排序非常有用。

5. 计数排序的优缺点

  1. 优点:
    🍉时间复杂度低:在适合的情况下,能够达到 O ( n ) O(n) O(n) 的线性时间复杂度。
    🍉稳定性:保持了相同元素的相对顺序,对数据处理有额外需求时非常有用。
  2. 缺点:
    🍇限制范围:计数排序只能用于整数类型数据不适用浮点数类型、字符类型的数据等,且适用于数据范围较小的情况。如果数据范围过大,空间复杂度会急剧增加。
    🍇额外空间:需要额外的空间来存储计数数组,尤其在范围较大时,空间消耗会非常明显。

6.计数排序的应用场景

由于计数排序对元素范围有一定限制,它更适用于以下场景:

  • 成绩统计:假设一个班级的学生成绩是 0-100 分的整数,那么使用计数排序能够快速对这些分数进行排序。

  • 投票计数:如果投票结果是有限个选项,可以用计数排序来统计每个选项的票数。

  • 基数排序的子过程:在基数排序中,计数排序通常被用作处理每一位数的排序过程。

好了,到这里本文就结束了。觉得本文对你有帮助的话,麻烦给偶点个赞吧!

haahah


http://www.kler.cn/news/366431.html

相关文章:

  • OpenCV视觉分析之目标跟踪(1)计算密集光流的类DISOpticalFlow的介绍
  • AIGC:开启智能创造的璀璨新篇章
  • vue文件报Cannot find module ‘webpack/lib/RuleSet‘错误处理
  • TensorFlow面试整理-TensorFlow 数据处理
  • 基于.NET 8.0,C#中Microsoft.Office.Interop.Excel来操作office365的excel
  • 在C及其他语言里运行shell命令的一点心得
  • Flink CDC系列之:学习理解核心概念——Data Pipeline
  • MySQL 二进制和中继日志管理
  • STM32L031F6P6开发环境搭建
  • 隨筆 20241023 Kafka 的幂等性与分区顺序性探讨
  • excel斜线表头
  • python爬虫:实例讲解xpatch的基本使用
  • 人工智能在自然语言处理(NLP)中的应用
  • Redis面试题扩展
  • .NET Core WebApi第2讲:前后端分离,Restful
  • unity URP下VolumetricFog插件发布的时候安卓里没有显示问题
  • C++二级2021年9月试卷及答案
  • Python——脚本实现datax全量同步mysql到hive
  • (北京政务服务满意度公司)满意度调查助力服务质量提升
  • 【Java】类来管理个人简历信息
  • UWB物资定位标签
  • C2W4.LAB.Word_Embedding.Part1
  • 智能听诊器:宠物医疗行业的新动力
  • ubuntu修改默认开机模式(图形/终端)
  • lucene数据写入-02倒排数据缓存组织
  • 【c++ arx 选项板2】