一篇文章快速搞懂计数排序

之前的几篇讲解了所有常用的比较排序,这次就来讲一讲非比较排序的计数排序。

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。和其他排序不一样,计数排序并不是通过比较元素的大小来实现的,而是通过统计所有相同元素出现的次数来进行排序,思路与其他的排序大有区别。

例如:

int arr[] = {2, 5, 3, 0, 2, 3, 0, 3}

在这里插入图片描述
我们利用哈希表的直接定址法, 如果某个元素出现过一次, 则在对应数值的下标处加一。

在这里插入图片描述
0出现了2次,1没有出现,2出现了两次,3出现了三次,4没有出现,5出现了一次,这样我们就统计到了所有元素出现的次数。

接下来将所有元素按照出现的次数重新覆盖进原数组中
在这里插入图片描述
这样就完成了排序。

从上面来看,这个算法的时间复杂度是非常低的,甚至能够前面讲过的所有排序,但是也可以很清楚的看出来,关于表长的选择还是存在着问题,因为上面那种方法选择的表长就是数据中出现的最大数据的大小,这样的话空间的消耗也会非常的大。

所以我们还有优化的方法,我们可以通过查找数据中的最大最小值,将其相减,即可确认我们所有数据所在的区间,这样就可以大大的减少空间的损失,最后回归数组的时候再将最小值加上,这样就大大的节约了空间
在这里插入图片描述
最后只需要将对应位置下标加上最小值,即可得到原来的数据,这样空间复杂度就从O(max)降为了O(range)

void CountSort(int* arr, int n)
{
	int i, j =0, min = arr[0], max = arr[0];
	
	for (i = 0; i < n; i++)
	{
		if (arr[i] < min)
			min = arr[i];

		if (arr[i] > max)
			max = arr[i];
	}
	//找到最大最小值

	int range = max - min + 1;
	//确认区间
	int* count = (int*)malloc(sizeof(int) * range);
	memset(count, 0, sizeof(int) * range);

	for (i = 0; i < n; i++)
	{
		//在对应位置记录出现次数
		count[arr[i] - min]++;
	}
	//将元素出现次数记录进表中

	for (i = 0; i < range; i++)
	{
		//将该下标所有元素放回原数组
		while (count[i]--) 
		{
			arr[j++] = i + min;
		}
	}
	//数据复写进原数组中
}

时间复杂度:Ο(n+k)(k为数据范围)
空间复杂度:O(K)

这样的算法虽然时间复杂度非常低,但是空间复杂度确十分高,是用空间来换取时间的做法

发布了60 篇原创文章 · 获赞 78 · 访问量 6322

猜你喜欢

转载自blog.csdn.net/qq_35423154/article/details/105209329