数据结构——lesson13排序之计数排序

简介: 数据结构——lesson13排序之计数排序

💞💞 前言

hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹

前面我们学习过七种排序——直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序、快速排序和归并排序,它们都是通过两数之间进行比较来排序的,今天我们就来学习非比较排序中的计数排序🥳🥳🥳

1.计数排序

基本思想

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

我们这里利用malloc开辟一个数组来统计相同元素出现的次数,用该数字下标表示相同元素,下标对应的值来统计次数

图示如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void CountSort(int* a, int n)
{
  //开辟数组
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
    perror("malloc fail");
    return;
  }
  //将tmp数组的值初始化为0
  for (int i = 0; i < n; i++)
  {
    tmp[i] = 0;
  }
  //遍历a
  for (int i = 0; i < n; i++)
  {
    //tmp下标对应值要++
    tmp[a[i]]++;
  }

  //拷贝回元素组a
  int j = 0;//记录a下标
  for (int i = 0; i < n; i++)
  {
    while (tmp[i]--)
    {
      a[j++] = i;
    }
  }
  free(tmp);
}
int main()
{
  int a[] = { 1,3,3,9,7,5,8,7,6 };
  printf("排序前:");

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

  CountSort(a, 9);
  printf("排序后:");
  for (int i = 0; i < 9; i++)
  {
    printf("%d ", a[i]);
  }
  printf("\n");
  return 0;
}

结果如下:

🥲我们仔细观察发现我们开辟tmp数组的大小是n:

int* tmp = (int*)malloc(sizeof(int) * n);

而数组a里面有九个数,也就是tmp大小为9,下标最大也就是8,那么当a中出现比8大的数9时应该怎么计数呢?就不可以计数了,所以出错了;

✨✨那么我们应该开辟多大的数组呢?应该根据什么来开辟才可以呢?

根据a数组最大最小值之差来开辟好像可以,a数组之间的范围就可以作为判断标准;但是这次我们得考虑得全面一点,如果a数组是这样得:a[] = {45,43,36,50,49,44,47}这些呢?那我们岂不是要开辟50个int大小的数组才可以有这么大的下标,如果是考虑范围就是最大50-最小36 = 14,更不可以了;

✨✨解决办法

利用相对值,还是开辟最大-最小的范围大小数组,然后最后拷贝数据的时候让下标+最小的数即可:

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void CountSort(int* a, int n)
{
  //求a数组最大最小差的范围
  int small = a[0];
  int big = a[0];
  for (int i = 1; i < n; i++)
  {
    //求最大值
    if (a[i] > big)
    {
      big = a[i];
    }
    //求最小值
    if (a[i] < small)
    {
      small = a[i];
    }
  }
  //范围
  
  int gap = big - small;
  //比如0~4,差就是4,但是对应开辟的大小得是5,0~4有五个数
  //开辟数组
  int* tmp = (int*)malloc(sizeof(int) * (gap+1));
  if (tmp == NULL)
  {
    perror("malloc fail");
    return;
  }
  //将tmp数组的值初始化为0
  for (int i = 0; i < gap+1; i++)
  {
    tmp[i] = 0;
  }
  //遍历a
  for (int i = 0; i < n; i++)
  {
    //tmp下标(a数组对应值-small)对应值要++
    tmp[a[i]-small]++;
  }

  //拷贝回元素组a,记得+samll
  int j = 0;//记录a下标
  for (int i = 0; i < gap+1; i++)
  {
    while (tmp[i]--)
    {
      a[j++] = i + small;
    }
  }
  free(tmp);
}
int main()
{
  int a[] = { 1,3,3,9,7,5,8,7,6 };
  printf("排序前:");

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

  CountSort(a, 9);
  printf("排序后:");
  for (int i = 0; i < 9; i++)
  {
    printf("%d ", a[i]);
  }
  printf("\n");
  return 0;
}


结果如下:

当int a[] = {45,43,36,50,49,44,47}时结果如下:

可以发现,计数排序成功啦~🥳🥳🥳

2.计数排序复杂度分析

2.1空间复杂度

我们根据上述代码实现可以知道计数排序开辟了大小为gap的数组,而gap对应的是最大与最小值的差也就是范围,所以其空间复杂度应该为O(gap);

2.2时间复杂度

时间复杂度:

①求数组a最大最小值时遍历了一遍数组a,次数为n;

②初始化tmp数组为0时遍历了数组tmp,次数为gap;

③统计下标出现次数时遍历数组a,次数为n;

④拷贝回原数组时,遍历了数组tmp,次数为gap;

所以其时间复杂度应该是n+gap+n+gap,简化为O(n+gap);

3.计数排序缺陷分析

前面我们学习的七大排序,时间复杂度最好也要O(n*logn);

而计数排序时间复杂度却可以达到O(n);

俗话说金无足赤,人无完人;计数排序达到这么好的时间复杂度其对应的缺陷也是非常明显的:

💥 缺陷1:依赖数据范围,适用于范围集中的数组

💥 缺陷2:只能用于整形(因为使用数组下标来统计)

所以计数排序使用的条件是非常苛刻的

4.结语

计数排序的关键在于理解并运用它的思想, 以上就是计数排序的介绍与实现啦~,完结撒花 ~🥳🥳🎉🎉🎉

相关文章
|
10月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
771 29
|
11月前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
379 10
|
11月前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
272 10
|
11月前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
300 7
|
搜索推荐 C++
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(一)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
163 5
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
280 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
166 1
|
搜索推荐 索引
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(二)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
181 4
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
|
人工智能 搜索推荐 算法
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(三)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
121 0

热门文章

最新文章