计数排序的理解与实现

    计数排序是一种非基于比较的排序算法,其空间复杂度和时间复杂度均为O(n+k),其中k是整数的范围。基于比较的排序算法时间复杂度最小是O(nlogn)的。注意:计数排序对于实数的排序是不可行的(下面会解释)。该算法于1954年由 Harold H. Seward 提出。

    下面根据一个示例来讲解,比如现在有个待排序的整数序列A={-1, 2, 0, 4, 3, 6, 5, 8, -2, 1, 3, 0, 3,6, 5, 2}。首先我们花O(n)的时间扫描一下整个序列,可以得到max=8,min=-2。然后我们建立一个新的数组C,长度为(max-min+1)=11。

数组C如下所示

    数组index为0的元素记录的值是-2出现的次数,依次index为1的元素记录的是-1出现的次数。

    此时我们再扫描一下数组A,比如对于-1,我们的操作是:-1-min=-1-(-2)=1;C[1]++。对于2,我们的操作是:2-(-2)=4;C[4]++。这样我们又花了O(n)的时间。操作结果是:


    最后我们便可以输出目标整数序列了,具体的逻辑是遍历数组C,比如C[0]=1,则输出一次min+index即(-2+0)=-2;C[1]=1,则输出一次-1;C[2]=2,则输出两次0,以此类推。目标序列为:

-2, -1, 0, 0, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 8

       分析可得,我们扫描了两次数组A,一次数组C,所以计数排序的时间复杂度为2(n)+n+k(扫描数组C的时间复杂度是n+k)。空间复杂度是:n+k(n是数组A的空间,最后输出目标整数序列时可以直接存储在A中;k是数组C的空间)。故其空间复杂度和时间复杂度均为O(n+k)。

       前面说了计数排序对于实数的排序是不可行的,这是因为我们无法根据最小值和最大值来确定数组C的长度,比如0.1和0.2之间有无限个实数。但是如果限制实数精度,依然是可行的,比如说数组A中的数字只保留5位小数。但是这已经不是实数了,相当于整数,因为相当于给原来数组A的所有数都乘以10^5。

具体的Java代码如下所示(countSort1在空间上作了一点优化,countSort2是最原版的实现,countSort1是上述描述的实现)

public class CountSortTest {

	public static void main(String[] args) {
		
		int [] A = {-1, 2, 0, 4, 3, 6, 5, 8, -2, 1, 3, 0, 3, 6, 5, 2};
		int [] B = countSort2(A);
		for(int index = 0; index < B.length; index++)
			System.out.print(B[index] + "  ");
		System.out.println();
		countSort1(A);
		for(int index = 0; index < A.length; index++)
			System.out.print(A[index] + "  ");
		
	}
	
	private static void countSort1(int [] A) {
		int min = 0, max = 0;
		int [] C = null;
		for(int index = 0; index < A.length; index++) {
			if(A[index] < min) {
				min = A[index];
				continue;
			}
			if(A[index] > max)	max = A[index];
		}
		C = new int[max - min + 1];
		for(int index = 0; index < A.length; index++) {
			C[A[index] - min]++;
		}
		int a = 0;
		for(int index = 0; index < C.length; index++) {
			for(int count = 0; count < C[index]; count++){
				A[a++] = index + min;
			}
		}
	}
	
	private static int [] countSort2(int [] A) {
		int min = 0, max = 0;
		int [] C = null;
		int [] B = new int[A.length];
		for(int index = 0; index < A.length; index++) {
			if(A[index] < min) {
				min = A[index];
				continue;
			}
			if(A[index] > max)	max = A[index];
		}
		C = new int[max - min + 1];
		for(int index = 0; index < A.length; index++) {
			C[A[index] - min]++;
		}
		for(int index = 1; index < C.length; index++){
			C[index] = C[index] + C[index - 1];
		}
		for(int index = A.length - 1; index > -1; index--) {
			B[C[A[index] - min] - 1] = A[index];
			C[A[index] - min]--;
		}
		return B;
	}
}

程序运行结果:




  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值