计数排序(counting-sort)——算法导论(9)
1. 比较排序算法的下界
到目前为止,我们已经介绍了几种能在O(nlgn)时间内排序n个数的算法:归并排序和堆排序达到了最坏情况下的上界;快速排序在平均情况下达到该上界。
如果仔细观察,我们会发现:在排序的最终结果中,各元素之间的次序依赖于它们之间的比较。我们把这类排序算法统称为比较排序。到目前为止我们介绍的排序算法都是比较排序。下面我们来论证一个事实:任何比较排序算法在最坏情况下都要经过Ω(n lgn)次比较。
在证明之前,我们先介绍一种由比较排序抽象而来的决策树模型。决策树是一棵完全二叉树,它可以表示在给定输入规模情况下,某一特定排序算法对所有元素的比较操作,而控制,数据移动等操作都被忽略了。如下图,它显示的是插入排序算法作用于包含三个元素的输入序列的决策树情况。

从决策树中我们可以看出:从根结点到任意一个可到达叶结点之间的最长简单路径的长度,表示的就是对应排序算法中最坏情况下的比较次数。因此,一个比较排序算法中的最坏情况的排序次数就等于决策树的高度。并且,当决策树中所有排列都是以可到达的叶结点的形式出现时,该决策树高度的下界也就是比较排序算法运行时间的下界。下面我们正式给出证明。
考虑一棵高度为h,具有l个可到达叶结点的决策树。它对应一个对n个元素进行的比较排序。因为输入数据有n!种可能的排列都是叶结点,所以n!≤l。由于在一棵高度为h的二叉树中,叶结点的数目不多于2^h,我们得到:
n! ≤ l ≤ 2^h,
两边取对数得:
h ≥ lg(n!) = Ω(nlgn)
2. 计数排序
我们先假设待排序序列各元素均在区间[0, k]上。
计数排序的思想是:在待排序序列中,如果我们能统计出有多少元素小于或等于某一个元素,我们也就知道了该元素的正确位置。例如,对于待排序序列{2,5,3,0,2,3,0,3},我们统计出有8个元素小于等于5(包括5自己),那么5这个元素就应该被排序到第8位。
下面给出算法的伪代码描述:

其中数组A[1~n]是待排序数组;数组B[1~n]用来存放已排好序的元素。C[0~k]用来存放上面所说的统计数(具体的说C[i]就表示在数组A中,小于或等于i的元素的总个数)。
下面这幅图描述的是对序列{2,5,3,0,2,3,0,3}排序的过程:

下面我们给出算法的Java实现代码:
public static void main(String[] args) {
	int[] array = { 2, 5, 3, 0, 2, 3, 0, 3 };
	printArray(countingSort(array, 5));
}
/**
 * 计数排序
 *
 * @param array
 *            待排序数组(假定各元素的范围是0~max,包括0和max)
 * @param max
 *            待排序数组中的最大值
 */
public static int[] countingSort(int[] array, int max) {
	int[] result = new int[array.length];
	int[] temp = new int[max + 1];
	// 以下循环操作完成后,temp的第i个位置保存着array中,值为i的元素的总个数
	for (int i : array) {
		temp[i]++;
	}
	// 以下循环操作完成后,temp的第i个位置保存着array中,值小于或等于i的元素的总个数
	for (int i = 1; i < temp.length; i++) {
		temp[i] += temp[i - 1];
	}
	for (int i = array.length - 1; i > -1; i--) {
		result[temp[array[i]] - 1] = array[i];
		temp[array[i]]--;
	}
	return result;
}
/**
 * 打印数组
 */
public static void printArray(int[] array) {
	for (int i : array) {
		System.out.print(i + " ");
	}
	System.out.println();
}
3. 算法分析
我们现在来分析计数排序的时间代价。
在伪代码中,第2~3行时间代价θ(k);第4~5行时间为θ(n);第7~8行时间为θ(k),第10~12行时间为θ(n)。因此,总的运行时间是θ(k+n)。当k= O(n)时,运行时间为θ(n)。
可以看出,计数排序的下界优于我们上面论证的比较排序算法的下界时间Ω(nlgn)。这是因为计数排序并不是比较排序算法。事实上,在代码中从未出现比较某两个元素大小的代码。相反,计数排序是使用输入元素的实际值来确定其在数组中的位置。此时,比较排序算法的模型对计数排序不再适用。
计数排序(counting-sort)——算法导论(9)的更多相关文章
- 《算法导论》——计数排序Counting Sort
		今天贴出的算法是计数排序Counting Sort.在经过一番挣扎之前,我很纠结,今天这个算法在一些scenarios,并不是最优的算法.最坏情况和最好情况下,时间复杂度差距很大. 代码Countin ... 
- 计数排序/Counting Sort
		计数排序的算法思想: 对于每一个元素x,只要确定了元素x有多少个比它小的元素,那么就可以知道其最终的位置. 记输入数组为A[n],存放最后排序输出的数组为B[n],提供临时存储空间的中间数组记为C[k ... 
- 计数排序Counting sort
		注意与基数排序区分,这是两个不同的排序 计数排序的过程类似小学选班干部的过程,如某某人10票,作者9票,那某某人是班长,作者是副班长 大体分两部分,第一部分是拉选票和投票,第二部分是根据你的票数入桶 ... 
- 地精排序(Gnome Sort) 算法
		gnome应该是最简单排序的排序算法吧!Gnome Sort,这是该算法的作者命名的,O(n*n)时间复杂度,O(1)空间复杂度,属于稳定的排序算法.算法的思想是每趟循环找到第一个逆序的元素,把它和在 ... 
- 转载 从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
		转载自:http://www.cnblogs.com/cj695/p/3863142.html sort函数在使用中非常好用,也非常简单,而且效率与冒泡或者选择排序不是一个数量级.本文就sort函数在 ... 
- 【转】 从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
		sort函数在使用中非常好用,也非常简单,而且效率与冒泡或者选择排序不是一个数量级.本文就sort函数在vector中的用法分为sort函数入门用法与自定义comp比较函数比较结构体这两个最基本的功能 ... 
- 从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
		sort函数在使用中非常好用,也非常简单,而且效率与冒泡或者选择排序不是一个数量级.本文就sort函数在vector中的用法分为sort函数入门用法与自定义comp比较函数比较结构体这两个最基本的功能 ... 
- 【C++】从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
		sort函数在使用中非常好用,也非常简单,而且效率与冒泡或者选择排序不是一个数量级.本文就sort函数在vector中的用法分为sort函数入门用法与自定义comp比较函数比较结构体这两个最基本的功能 ... 
- 跳跃空间(链表)排序 选择排序(selection sort),插入排序(insertion sort)
		跳跃空间(链表)排序 选择排序(selection sort),插入排序(insertion sort) 选择排序(selection sort) 算法原理:有一筐苹果,先挑出最大的一个放在最后,然后 ... 
- 【算法导论】【排序】—— 计数排序(counting sort)
		计数排序的特点: 需要额外的数组以存储: 中间过程数据(记为数组 C),数组 C 的下标是待排序序列的元素值,下标对应的值为出现的次数: 排序后的序列(记为 B),计数排序仅获取原始待排序序列的值,对 ... 
随机推荐
- 前端极易被误导的css选择器权重计算及css内联样式的妙用技巧
			记得大学时候,专业课的网页设计书籍里面讲过css选择器权重的计算:id是100,class是10,html标签是5等等,然后全部加起来的和进行比较... 我只想说:真是误人子弟,害人不浅! 最近,在前 ... 
- 拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?
			写在前面 除了博文内容之外,和 netfocus 兄的讨论,也可以让你学到很多(至少我是这样),不要错过哦. 阅读目录: 迷雾森林 找回自我 开源地址 后记 毫无疑问,领域驱动设计的核心是领域模型,领 ... 
- C语言  ·  矩形面积交
			问题描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入格式 输入仅包含两行,每行描述一个矩形. 在每行中 ... 
- ExtJS 4.2 Grid组件的单元格合并
			ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ... 
- ASP.NET Core应用中如何记录和查看日志
			日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ... 
- [原] Cgroup CPU, Blkio 测试
			关于Cgroup的简单测试 [toc] 简单介绍Cgroup (如果对cgroup熟悉可以忽略) 一般情况下,cgroup挂载到一个虚拟文件目录,然后可以通过文件系统的API对其操作. ># m ... 
- BPM与 SAP & Oracle EBS集成解决方案分享
			一.需求分析 SAP和Oracle EBS都是作为全球顶级的的ERP产 品,得到了众多客户的青睐.然而由于系统庞大.价格昂贵以及定位不同,客户在实施过程中经常会面临以下困惑: 1.SAP如何实现&qu ... 
- Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)
			之前的博文<Android中使用ExpandableListView实现好友分组>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信 ... 
- 信息安全-2:python之hill密码算法[原创]
			转发注明出处:http://www.cnblogs.com/0zcl/p/6106513.html 前言: hill密码算法我打算简要介绍就好,加密矩阵我用教材上的3*3矩阵,只做了加密,解密没有做, ... 
- Win10命令提示符(cmd)怎么复制粘贴
			在Win10系统里右键开始菜单,选择弹出菜单里的命令提示符,如下图所示: 然后复制要粘贴的文字,例如: echo hovertree.com 把上面的文字复制后,点击命令提示符窗口,然后在命令提示符窗 ... 
