之前文章介绍的一些排序算法都是基于比较来进行排序的,故它们在平均情况下的时间复杂度最好也不过是线性对数级别。这里我们来介绍一种简单的基于非比较的排序算法——Counting Sort 计数排序,其时间复杂度可以达到线性级别

基本思想

Counting Sort 计数排序,顾名思义,就是统计待排序数组元素的出现次数。其基本思想比较简单: 1. 根据待排序元素的数值范围大小k(max-min+1),建立一个k大小的频数统计数组counts。对于counts数组而言,其索引范围 0 ~ k-1,正好可以对应到待排序元素的取值范围min~max上 2. 统计待排序元素element的次数,并其存储到counts数组中,即counts[ elemet-min ] = countValue 3. 待计数统计完成后遍历counts数组,根据次数值来输出原待排序元素值,此时即完成排序

这里给出计数排序在Java下的实现:

/*** 计数排序*/
public class CountingSort {/*** 升序排列 (非稳定)*/public static void sort1() {// 获取测试用例int[] array = getTestCase();int size = array.length;System.out.println("before sort: " + Arrays.toString(array) );// 求取最值int[] minMax = getMinMax(array);int min = minMax[0];int max = minMax[1];// 根据数据范围创建统计数组int k = max-min+1;int[] counts = new int[ k ];// 统计原数组中元素出现的次数for(int i=0; i<size; i++) {int dataInCountIndex = array[i] - min;    // 计算原数组元素在统计数组中的索引counts[dataInCountIndex] +=1;             // 统计该值次数}int j = 0;  // 有序的数据的插入位置for(int i=0; i<k; i++) {int originalData = i + min; // 还原 原排序数组的数据值while( counts[i]>0 ) {array[j] = originalData;counts[i]--;    // 该数值出现的次数减1j++;            // 更新有序数据的插入位置}}System.out.println("after sort: " + Arrays.toString(array) );}  /*** 求指定数组的最值* @param array* @return [0]: 最小值; [1]: 最大值;*/private static int[] getMinMax(int[] array) {int min = array[0];int max = array[0];for(int i=1; i<array.length; i++) {if( array[i]>max ) {max = array[i];}if( array[i]<min ) {min = array[i];}}int[] minMax = new int[]{min,max};return minMax;}/*** 获取测试用例*/private static int[] getTestCase() {int[] caseArray = {-1,1,-1,1,2};return caseArray;}
}

测试结果如下:

稳定的计数排序

基本原理

上面的计数排序算法是非稳定的,而一般我们所说的计数排序都是稳定的。那么如何保证计数排序的稳定性呢?其实很简单,只需在统计完待排序元素的频数后,对counts作累加计算(counts[i] = counts[i-1] + counts[i]),即计算统计数组中指定索引位置上的元素在排序后的位置;然后倒序遍历原数组,根据counts数组中的排序位置来将待排序元素放入合适的位置,同时将counts数组相应的值减1,以使下一个重复的排序元素可以放在前一位的位置上,以保证稳定性

算法图解

下图即是通过稳定的计数排序对 -1,1,-1,1,2 序列进行升序排列的图解过程

1. 建立counts数组

2. 倒序遍历原待排序数组,按升序排列

Java实现

这里给出计数排序在Java下的实现:

/*** 计数排序*/
public class CountingSort {.../*** 升序排列 (稳定)*/public static void sort2() {// 获取测试用例int[] array = getTestCase();int size = array.length;System.out.println("before sort: " + Arrays.toString(array) );// 求取最值int[] minMax = getMinMax(array);int min = minMax[0];int max = minMax[1];// 根据数据范围创建统计数组int k = max-min+1;int[] counts = new int[ k ];// 统计原数组中元素出现的次数for(int i=0; i<size; i++) {int dataInCountIndex = array[i] - min;    // 计算原数组元素在统计数组中的索引counts[dataInCountIndex] +=1;             // 统计该值次数}// 计算统计数组的累计值,即计算 统计数组中指定索引位置上的元素在排序后的位置// 如果该索引位置上有重复元素,则为重复元素所占的最大排序位置for(int i=1; i<k; i++) {counts[i] = counts[i-1] + counts[i];}int[] sortedArray = new int[size];  // 排序结果数组// 倒序遍历原数组,保证稳定性for(int i=size-1; i>=0; i--) {int dataInCountIndex = array[i] - min;  // 计算原数组元素在统计数组中的索引// 计算其排序后的位置, 因为数组索引从0开始计算,故应对排序位置减1// 例如,排在最前面的元素,排序位置为1,则其在数组中的位置索引应为0int sortIndex = counts[dataInCountIndex] - 1;sortedArray[sortIndex] = array[i]; // 将原数组元素放入排序后的位置上counts[dataInCountIndex]--;        // 下一个重复的元素,应排前一个位置,以保证稳定性}System.out.println("after sort: " + Arrays.toString(sortedArray) );}...
}

测试结果如下:

特点

复杂度、稳定性

这里以稳定的计数排序进行说明:

缺陷

  1. 计数排序只能适用待排序元素为整数的场景
  2. 待排序元素的数值范围(极差)过大的情况下,计数排序会浪费大量空间,故一般不推荐使用计数排序

参考文献

  1. 算法导论 · 第3版

倒序排序_排序算法(六):Counting Sort 计数排序相关推荐

  1. C++counting sort计数排序(针对string)的实现算法(附完整源码)

    C++counting sort计数排序的实现算法 C++counting sort计数排序的实现算法完整源码(定义,实现,main函数测试) C++counting sort计数排序的实现算法完整源 ...

  2. C++counting sort计数排序的实现算法(附完整源码)

    C++counting sort计数排序的实现算法 C++counting sort计数排序的实现算法完整源码(定义,实现,main函数测试) C++counting sort计数排序的实现算法完整源 ...

  3. counting sort (计数排序) algorithm

    为什么80%的码农都做不了架构师?>>>    假设n个输入元素中每一个均介于0~k之间,k为最大值,这里k为整数.如果k=O(n), 则计数排序的运行时间为θ(n). 计数排序的核 ...

  4. c++ 二维数组 排序_【算法】排序算法之计数排序

    前几回,我们已经对[算法]排序算法之冒泡排序.[算法]排序算法之插入排序.[算法]排序算法之希尔排序.[算法]排序算法之选择排序.[算法]排序算法之快速排序.[算法]排序算法之归并排序.[算法]排序算 ...

  5. 排序算法:基数排序与计数排序

    基数排序是基于计数排序的算法,每次都需要使用计数排序从而实现基数排序. 那么什么叫基于计数排序?我们首先要明白基数排序的原理: 每次对数字的一个数位(个位.十位.百位......)进行比较,每次比较后 ...

  6. java sorted排序_【算法】排序算法之计数排序

    前几回,我们已经对冒泡排序.直接插入排序.希尔排序.选择排序.快速排序.归并排序.堆排序做了说明分析.本回,将对计数排序进行相关说明分析. 一.排序算法系列目录说明 冒泡排序(Bubble Sort) ...

  7. Java冒泡,快速,插入,选择排序^_^+二分算法查找

    这段时间在学Java,期间学到了一些排序和查找方法.特此写来和大家交流,也方便自己的日后查看与复习. 1.下边是Java的主类: public class Get {public static voi ...

  8. 如何生成有向图_八十六、从拓扑排序探究有向图

    「@Author:Runsen」 关于排序,其实还有很多,比如常见的希尔排序,桶排序,计数排序和基数排序,由于要过渡到数据结构有向图,因此需要了解拓扑排序和邻接矩阵概念. 拓扑排序 拓扑排序本身并不是 ...

  9. java集合中中文排序_利用Collator和Collections.sort对list进行中文排序,注意与Arrays.sort的区别...

    //两者的关系:1.Coollections.sort的内部实现是用Arrays.sort来实现的. //2.如果要排序的list中的对象已经实现了Comparable接口,那么可以用Arrays.s ...

最新文章

  1. 样本方差除以n-1而不是n的原因
  2. pat1033汽车加油问题(Java贪心)
  3. WPF的ProgressBar进度条
  4. React中的方法调用
  5. php system 返回值126,exec – PHP return_val是126
  6. 数字猜谜游戏python_Python Tkinter教程系列02:数字猜谜游戏
  7. 将一个类改成线程_看了这个有趣的例子,相信你就秒懂多线程同步了
  8. SkyEye仿真ZYNQ芯片,轻松运行国产操作系统ReWorks
  9. mysql添加标签_PHP / MySQL - 如何添加多个标签
  10. css span 右端对齐_使用 CSS 实现具有方面感知的幽灵按钮
  11. 语音识别的应用都有哪些
  12. Restful规范-开发api接口
  13. Codeforces GoodBye2015 New Year and Three Musketeers Codeforces 611E(贪心)
  14. Android简单实现百度地图显示及定位
  15. 最小生成树的实现(C语言)
  16. 阿里云大学云学院 “人工智能” 专业重磅预售
  17. ARM7——LPC2xxx小总结
  18. 骨传导耳机的缺点有哪些?骨传导耳机的优缺点分析
  19. Python之计算π值
  20. Mac下浏览器安装证书

热门文章

  1. 牛客网 最短路 Floyd算法 Dijkstra算法 Java大数
  2. HDU 1556 前缀和 树状数组 线段树
  3. Python-类的学习
  4. 回归分析残差不满足正态分布_线性回归思路梳理!精华必看!
  5. vue 初始化方法_Vue源码解读(一)引入Vue做了什么
  6. 实验4 贪心法(作业调度问题)
  7. Vue + Element UI——侧边栏LOGO设计DEMO
  8. java web如何配置ask_Javaweb新手之路之JavaWeb开发环境配置篇
  9. JVM调优技巧与经验
  10. Hadoop和Spark生态圈了解