【数据结构】非比较排序算法(实现计数排序和基数排序)

● 计数排序

1、算法思想:

计数排序是直接定址法的变形。通过开辟一定大小的空间,统计相同数据出现的次数,然后回写到原序列中。

2、步骤:

1)找到序列中的最大和最小数据,确定开辟的空间大小。

2)开辟空间,利用开辟的空间存放各数据的个数。

3)将排好序的序列回写到原序列中。

具体实现如下:

void CountSort(int *arr, int size)
{
 assert(arr);
 int min = arr[0];
 int max = arr[0];
 int num = 0;
 for (int i = 0; i < size; ++i)//找出最大和最小数
 {
  if (arr[i] < min)
  {
   min = arr[i];
  }
  if (arr[i] > max)
  {
   max = arr[i];
  }
 }
 num = max - min + 1;//开辟的空间大小
 int *count = new int[num];
 memset(count, 0, sizeof(int)*num);//初始化count
 for (int i = 0; i < size; ++i)
 {
  count[arr[i] - min]++;//直接定址
 }
 int index = 0;
 for (int i = 0; i < num; ++i)
 {
  while (count[i]--)
  {
   arr[index] = i + min;//回写到原序列中
   index++;
  }
 }
}

优缺点:

优势:在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法

劣势:基数排序需要开辟对应大小的空间,k较大时空间利用率不高,故适应于数据比较密集的序列。如果数据密集且没有重复,我们可以用位图实现。

● 基数排序

基数排序是典型的分配类排序,分配类排序是指利用分配和收集两种基本操作实现排序。基数排序通过反复的进行分配与收集操作完成排序,基数排序有两种排序方法,分别是“低位优先”和“高位优先”。在这里我以“低位优先”排序法进行分析。

1、算法思想:

排序时先按最低位的值对记录进行初步排序,在此基础上再按次低位的值进行进一步排序。以此类推,由低位到高位,每一趟都是在前一趟的基础上,根据关键字key的某一位对所有记录进行排序,直到最高位,这样就完成了基数排序的全过程。

2、步骤:

1)由于一直到最高位结束,故需找到最大数的位数。

2)开辟空间存放一趟排序后的序列。利用矩阵的快速转置思想,用count数组存放进行排序的位相同数字的个数,用start数组记录每次开始的位置,对每一个元素进行快速定位。

3)回写到原序列中。

4)重复以上步骤,直到比较到最高位为止。

具体实现如下:

void RadixSort(int *arr, int size)
{
 assert(arr);
 int *count = new int[10];//每位的数字在0~9之间
 int *start = new int[10];
 int *tmp = new int[size];//存放每趟排序后的序列
 int MaxRadix = GetMaxRadix(arr, size);//最大数的位数
 int radix = 1;
 for (int k = 1; k <= MaxRadix; ++k)
 {
  memset(count, 0, sizeof(int)* 10);//初始化count
  memset(start, 0, sizeof(int)* 10);
  for (int i = 0; i < size; ++i)//count存放进行排序的位数相同的个数
  {
   count[(arr[i] / radix) % 10]++;
  }
  start[0] = 0;
  for (int i = 1; i < 10; ++i)//start存放数据开始位置
  {
   start[i] = start[i - 1] + count[i - 1];
  }
  for (int i = 0; i < size; ++i)//快速定位
  {
   int num = (arr[i] / radix) % 10;
   tmp[start[num]++] = arr[i];
  }
  memcpy(arr, tmp, sizeof(int)*size);//回写
  radix *= 10;
 }
 delete[] tmp;//注意释放tmp
}

● 稳定性分析

稳定性指排序后在原序列中相同数据的相对位置不会发生改变。插入排序、冒泡排序、归并排序、计数排序和基数排序是稳定的;快速排序、希尔排序、堆排序和选择排序是不稳定的。

● 复杂度分析

1、空间复杂度:快速排序、归并排序、计数排序和基数排序都需要开辟空间,插入排序、希尔排序、选择排序、堆排序、冒泡排序都不需要,空间复杂度为O(1)。

2、时间复杂度:

1)插入排序、希尔排序、选择排序、冒泡排序都为O(n^2),效率较低。选择排序效率最低,在最好情况下时间复杂度还是O(n^2);冒泡排序和插入排序相比较,插入排序较好(eg:0 2 1 3 4 5 6 7 8 9;插入排序一次完成,而冒泡排序需要冒两次),冒泡排序代价较大,并且插入在进行优化(希尔排序)后更能缩短排序时间。

2)堆排序、归并排序和快速排序都是O(n*lg(n))。

通常都看最坏的情况,但快速排序(存在更多优化)几乎不存在最坏情况,时间复杂度为O(n*lg(n))。

堆排序有一个缺点就是只能对数组进行排序,基数排序和计数排序都存在局限性(数据密集),归并排序的空间复杂度为O(n),而快速排序为O(lg(n)),综合可得快速排序最好。

【干货】

归并排序存在内排序和外排序。外排序其实就是指能够对内存之外(磁盘中)数据进行排序,对于大数据的文件,不能够直接加载到内存中进行排序,可以采取将文件划分成小文件,将小的文件加载到内存中进行排序,然后将排好序的数据进行重写,将两个有序的数据文件在重新排序,就能够排好大数据文件。依据以上思想可进行文件压缩的实现,有兴趣的可以自己试试。

时间: 2024-10-25 06:56:20

【数据结构】非比较排序算法(实现计数排序和基数排序)的相关文章

排序算法之计数排序

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

常见排序算法之计数排序与基数排序

1.计数排序 顾名思义,是对待排序数组中的数据进行统计,然后根据统计的数据进行排序,例如: 待排序数组: a[] = { 100, 123, 112, 123, 201, 123, 112, 156, 156, 178, 185, 156, 123, 112 } 首先取出数组中最大值与最小值(这个不用说了吧) 最大值:201  最小值:100 因此该数组的数值范围为 201 - 100 + 1 = 102 开辟一个大小为102的数组 count[102] 并将所有元素初始化为0 再次遍历数组,以

【算法】计数排序、桶排序和基数排序详解

01.计数排序.桶排序与基数排序 并不是所有的排序 都是基于比较的,计数排序和基数排序就不是.基于比较排序的排序方法,其复杂度无法突破\(n\log{n}\) 的下限,但是 计数排序 桶排序 和基数排序是分布排序,他们是可以突破这个下限达到O(n)的的复杂度的. 1. 计数排序 概念 计数排序是一种稳定的线性时间排序算法.计数排序使用一个额外的数组C,使用 C[i] 来计算 i 出现的次数.然后根据数C来将原数组A中的元素排到正确的位置. 复杂度 计数排序的最坏时间复杂度.最好时间复杂度.平均时

算法导论之六:线性时间排序之 决策树&amp;计数排序

本系列前五篇都是讲述的比较排序算法,从本文开始,将进入线性时间排序.什么是比较排序,简单的说,就是排序的过程依赖于数组中数据大小的比较,从而来确定数据在排好序输出时的位置. 比较排序法比较直观,但是也有它的不足,我们容易证明任何比较排序法,在最坏的情况下的时间复杂度的下限都是 nlgn.要证明这个问题,我们首先要搞清楚一个模型:决策树模型. 一.决策树模型 什么是决策树?决策树从形态上来讲,是一颗完全二叉树,它除叶子节点之外,其他层的节点都是满的.它的每一个叶子节点表示对输入数据组合的一种排序可

排序算法下——桶排序、计数排序和基数排序

桶排序.计数排序和基数排序这三种算法的时间复杂度都为 $O(n)$,因此,它们也被叫作线性排序(Linear Sort).之所以能做到线性,是因为这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作. 1. 桶排序(Bucket Sort)? 1.1. 桶排序原理 桶排序,顾名思义,要用到"桶".核心思想是将要排序的数据分到几个有序的桶里,每个桶的数据再单独进行排序.桶内排完序后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了. 1.2. 桶排序的时间复杂度分析 如

三种线性排序算法(计数、基数、桶排序)的简单实现

一.计数排序 计数排序假设n个输入元素中的每一个都是介于0到k之间的整数.此处k为某个整数(输入数据在一个小范围内). 基本思想: 计数排序的基本思想是对每一个输入元素x,确定出小于x的元素的个数.然后再将x直接放置在它在最终输出数组中的位置上. 如下图所示: 由于数组中可能有相等的数,在处理时需要注意. 时间复杂度和空间复杂度分析 算法总时间Θ(k + n).当k=O(n)时,计数排序的运行时间是Θ(n). 空间复杂度是O(n+k).需要两个辅助数组:存放排序结果的数组B[n],存放临时结果的

数据结构排序算法之选择排序

今天继续介绍一种排序算法:选择排序. 选择排序的基本思想就是从待排序列中选择出最小的,然后将被选出元素和序列的第一个元素互换位置(当前默认是升序排列),则互换完成后第一个元素就是整个序列的最小的元素,则一次选择排序结束.然后我们从剩下的子序列中选择出最小的,然后将该被选出来的元素和该子序列的第一个元素(即整个序列的第二个元素)互换位置,则当前整个序列的第二个元素就是当前序列中的次最小值,第二次选择排序结束.以此类推,直到该待排序列只剩下一个元素后,则整个序列有序. 具体过程如下图所示: 下面就不

基本排序系列之计数排序

简述计数排序 看了好多别人写的计数排序,看了好久都没看懂,弄了好久最后发现这么简单居然花了几个小时,所以在这里写上,希望和我一样的初学者不会再绕弯路. 一.简述计数排序的思想: 设被排序的数组为A,排序后存储到B,C为临时数组.所谓计数,首先是通过一个数组C[i]计算大小等于i的元素个数,此过程只需要一次循环遍历就可以:在此基础上,计算小于或者等于i的元素个数,也是一重循环就完成.下一步是关键:逆序循环,从length[A]到1,将A[i]放到B中第C[A[i]]个位置上.原理是:C[A[i]]

最快最简单的排序算法:桶排序

在我们生活的这个世界中到处都是被排序过的.站队的时候会按照身高排序,考试的名次需要按照分数排序,网上购物的时候会按照价格排序,电子邮箱中的邮件按照时间排序……总之很多东西都需要排序,可以说排序是无处不在.现在我们举个具体的例子来介绍一下排序算法. 首先出场的我们的主人公小哼,上面这个可爱的娃就是啦.期末考试完了老师要将同学们的分数按照从高到低排序.小哼的班上只有5个同学,这5个同学分别考了5分.3分.5分.2分和8分,哎考的真是惨不忍睹(满分是10分).接下来将分数进行从大到小排序,排序后是8

排序算法之鸡尾酒排序

昨天中午北京温度为40度,在中午12:16分我来到篮球场,思考了1分钟决定开站 转球: 我和另外3名队友开始半场, 球传到我手的刹那顿时烫的我持球不稳,顿时问道了淡淡的胶皮味道和烤肉味道的混搭. 这时我来了一个腾空跳投, 球---------爆炸了........ 听新闻说昨天在路上都是 "熟人" 一位老大爷不慎被车刮倒了,大爷二话没说立马爬了起来,围观众人议论纷纷: "大爷人不错","大爷素质真高","大爷身体可真好" 大爷