倒序排序_排序算法(六):Counting Sort 计数排序
之前文章介绍的一些排序算法都是基于比较来进行排序的,故它们在平均情况下的时间复杂度最好也不过是线性对数级别。这里我们来介绍一种简单的基于非比较的排序算法——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) );}...
}
测试结果如下:
特点
复杂度、稳定性
这里以稳定的计数排序进行说明:
缺陷
- 计数排序只能适用待排序元素为整数的场景
- 待排序元素的数值范围(极差)过大的情况下,计数排序会浪费大量空间,故一般不推荐使用计数排序
参考文献
- 算法导论 · 第3版
倒序排序_排序算法(六):Counting Sort 计数排序相关推荐
- C++counting sort计数排序(针对string)的实现算法(附完整源码)
C++counting sort计数排序的实现算法 C++counting sort计数排序的实现算法完整源码(定义,实现,main函数测试) C++counting sort计数排序的实现算法完整源 ...
- C++counting sort计数排序的实现算法(附完整源码)
C++counting sort计数排序的实现算法 C++counting sort计数排序的实现算法完整源码(定义,实现,main函数测试) C++counting sort计数排序的实现算法完整源 ...
- counting sort (计数排序) algorithm
为什么80%的码农都做不了架构师?>>> 假设n个输入元素中每一个均介于0~k之间,k为最大值,这里k为整数.如果k=O(n), 则计数排序的运行时间为θ(n). 计数排序的核 ...
- c++ 二维数组 排序_【算法】排序算法之计数排序
前几回,我们已经对[算法]排序算法之冒泡排序.[算法]排序算法之插入排序.[算法]排序算法之希尔排序.[算法]排序算法之选择排序.[算法]排序算法之快速排序.[算法]排序算法之归并排序.[算法]排序算 ...
- 排序算法:基数排序与计数排序
基数排序是基于计数排序的算法,每次都需要使用计数排序从而实现基数排序. 那么什么叫基于计数排序?我们首先要明白基数排序的原理: 每次对数字的一个数位(个位.十位.百位......)进行比较,每次比较后 ...
- java sorted排序_【算法】排序算法之计数排序
前几回,我们已经对冒泡排序.直接插入排序.希尔排序.选择排序.快速排序.归并排序.堆排序做了说明分析.本回,将对计数排序进行相关说明分析. 一.排序算法系列目录说明 冒泡排序(Bubble Sort) ...
- Java冒泡,快速,插入,选择排序^_^+二分算法查找
这段时间在学Java,期间学到了一些排序和查找方法.特此写来和大家交流,也方便自己的日后查看与复习. 1.下边是Java的主类: public class Get {public static voi ...
- 如何生成有向图_八十六、从拓扑排序探究有向图
「@Author:Runsen」 关于排序,其实还有很多,比如常见的希尔排序,桶排序,计数排序和基数排序,由于要过渡到数据结构有向图,因此需要了解拓扑排序和邻接矩阵概念. 拓扑排序 拓扑排序本身并不是 ...
- java集合中中文排序_利用Collator和Collections.sort对list进行中文排序,注意与Arrays.sort的区别...
//两者的关系:1.Coollections.sort的内部实现是用Arrays.sort来实现的. //2.如果要排序的list中的对象已经实现了Comparable接口,那么可以用Arrays.s ...
最新文章
- 样本方差除以n-1而不是n的原因
- pat1033汽车加油问题(Java贪心)
- WPF的ProgressBar进度条
- React中的方法调用
- php system 返回值126,exec – PHP return_val是126
- 数字猜谜游戏python_Python Tkinter教程系列02:数字猜谜游戏
- 将一个类改成线程_看了这个有趣的例子,相信你就秒懂多线程同步了
- SkyEye仿真ZYNQ芯片,轻松运行国产操作系统ReWorks
- mysql添加标签_PHP / MySQL - 如何添加多个标签
- css span 右端对齐_使用 CSS 实现具有方面感知的幽灵按钮
- 语音识别的应用都有哪些
- Restful规范-开发api接口
- Codeforces GoodBye2015 New Year and Three Musketeers Codeforces 611E(贪心)
- Android简单实现百度地图显示及定位
- 最小生成树的实现(C语言)
- 阿里云大学云学院 “人工智能” 专业重磅预售
- ARM7——LPC2xxx小总结
- 骨传导耳机的缺点有哪些?骨传导耳机的优缺点分析
- Python之计算π值
- Mac下浏览器安装证书