计数排序和基数排序的实现

计数排序

计数排序的原理

设被排序的数组为A,排序后存储到B,C为临时数组。所谓计数,首先是通过一个数组C[i]计算大小等于i的元素个数,此过程只需要一次循环遍历就可以;在此基础上,计算小于或者等于i的元素个数,也是一重循环就完成。下一步是关键:逆序循环,从length[A]到1,将A[i]放到B中第C[A[i]]个位置上。原理是:C[A[i]]表示小于等于a[i]的元素个数,正好是A[i]排序后应该在的位置。而且从length[A]到1逆序循环,可以保证相同元素间的相对顺序不变,这也是计数排序稳定性的体现。在数组A有附件属性的时候,稳定性是非常重要的。

计数排序的前提及适用范围

A中的元素不能大于k,而且元素要作为数组的下标,所以元素应该为非负整数。而且如果A中有很大的元素,不能够分配足够大的空间。所以计数排序有很大局限性,其主要适用于元素个数多,但是普遍不太大而且总小于k的情况,这种情况下使用计数排序可以获得很高的效率。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。

当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

计数排序算法的步骤:

1.找出待排序的数组中最大和最小的元素

2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项

3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)

4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

实现代码:

void CountSort(int *a, int size)
{
	int min = a[0], max = a[0];
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		if (min > a[i])
		{
			min = a[i];//找出数组中最小的数
		}
		if (max < a[i])
		{
			max = a[i];//找出数组中的最大数
		}
	}
	int range = max - min + 1;
	int *count = new int[range];
	//初始化数组
	//memset(count, 0, sizeof(int)*range);
	for (i = 0; i < range; i++)
	{
		count[i] = 0;
	}
	//把数组a中数变成数组count中的0,1,2....
	for (i = 0; i < size; i++)
	{
		//把数组count中对应位置制成数字,代表这个位置有几个相同的数
		//列如制成1,代表这个位置有一个数
		//列如制成2,代表这个位置有两个相同的数
		count[a[i] - min]++;
	}
	int j = 0;
	//把count中的数还原回数组a中,它就排好序了
	for (i = 0; i < range; i++)
	{
		//重复了n次,就拿回去n次
		while (count[i]>0)
		{
			a[j++] = i + min;
			count[i]--;
		}
	}
	delete[] count;
}

基数排序

算法思想:

待排序数组[62,14,59,88,16]简单点五个数字

分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样

|  0  |  0  | 62 |  0  | 14 |  0  | 16 |  0  |  88 | 59 |

|  0  |  1  |  2  |  3  |  4 |  5  |  6  |  7  |  8  |  9  |桶编号

将桶里的数字顺序取出来,

输出结果:[62,14,16,88,59]

再次入桶,不过这次以十位数的数字为准,进入相应的桶,变成下边这样:

由于前边做了个位数的排序,所以当十位数相等时,个位数字是由小到大的顺序入桶的,就是说,入完桶还是有序

|  0  | 14,16 |  0  |  0  |  0  | 59 | 62  | 0  | 88  |  0  |

|  0  |  1      |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |桶编号

因为没有大过100的数字,没有百位数,所以到这排序完毕,顺序取出即可

最后输出结果:[14,16,59,62,88]

实现代码:

//获取最大位数
static int GetMaxRadix(int *a, int size)
{
	int radix = 10;
	int count = 1;
	for (int i = 0; i < size; i++)
	{
		//注意这里必须是">=",假如你的最大数是100,如果
		//没有“=”的话,你获取最大位就是两位
		while (a[i]>=radix)
		{
			radix *= 10;
			count++;
		}
	}
	return count;
}
static void _RadixSort(int* a, int size, int divisor, int* tmp)
{
	int count[10] = { 0 };
	int start[10] = { 0 };
	//如果你处理的是个位,count代表就是数据个位在
	//count对应位置出现的个数。十位,百位类似。
	for (int i = 0; i < size; i++)
	{
		int num1 = a[i] / divisor;
		count[num1 % 10]++;
	}
	//个位,十位,百位等出现的起始位置
	for (int j = 1; j < 10; j++)
	{
		start[j] = start[j - 1] + count[j - 1];
	}
	//根据start,将a中的数据放在tmp中,已排好序
	for (int k = 0; k < size; k++)
	{
		int num2 = a[k] / divisor;
		tmp[start[num2 % 10]++] = a[k];
	}
	//把排好序的数据放回a中
	for (int n = 0; n < size; n++)
	{
		a[n] = tmp[n];
	}
}
void RadixSort(int *arr, int size)
{
	assert(arr);
	int *tmp = new int[size];
	int n = GetMaxRadix(arr, size);
	for (int i = 1; i <= n; i++)
	{
		int divisor = 1;
		int k = i;
		while (--k)
		{
			divisor *= 10;
		}
		_RadixSort(arr, size, divisor, tmp);
	}
	delete[] tmp;
}
时间: 2024-10-12 23:52:50

计数排序和基数排序的实现的相关文章

【数据结构】非比较排序算法(实现计数排序和基数排序)

● 计数排序 1.算法思想: 计数排序是直接定址法的变形.通过开辟一定大小的空间,统计相同数据出现的次数,然后回写到原序列中. 2.步骤: 1)找到序列中的最大和最小数据,确定开辟的空间大小. 2)开辟空间,利用开辟的空间存放各数据的个数. 3)将排好序的序列回写到原序列中. 具体实现如下: void CountSort(int *arr, int size) {  assert(arr);  int min = arr[0];  int max = arr[0];  int num = 0;

计数排序、基数排序与桶排序

一.计数排序 稳定. 当输入的元素是n 个小区间(0到k)内整数时,它的运行时间是 O(n + k),空间复杂度是O(n). const int K = 100; //计数排序:假设输入数据都属于一个小区间内的整数,可用于解决如年龄排序类的问题 //Input:A[0, ..., n-1], 0 <= A[i] < K //Output:B[0, ..., n-1], sorting of A //Aux storage C[0, ..., K) void CountSort(int A[],

线性排序算法---- 计数排序, 基数排序, 桶排序

排序算法里,除了比较排序算法(堆排序,归并排序,快速排序),还有一类经典的排序算法-------线性时间排序算法.听名字就让人兴奋! 线性时间排序,顾名思义,算法复杂度为线性时间O(n) , 非常快,比快速排序还要快的存在,简直逆天.下面我们来仔细看看三种逆天的线性排序算法, 计数排序,基数排序和桶排序. 1计数排序  counting Sort 计数排序 假设 n个 输入元素中的每一个都是在 0 到 k 区间内的一个整数,当 k= O(N) 时,排序运行的时间为 O(N). 计数排序的基本思想

排序算法(七)非比较排序:计数排序、基数排序、桶排序

前面讲的是比较排序算法,主要有冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等. 非比较排序算法:计数排序,基数排序,桶排序.在一定条件下,它们的时间复杂度可以达到O(n). 一,计数排序(Counting Sort) (1)算法简介 计数排序(Counting sort)是一种稳定的排序算法.计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数.然后根据数组C来将A中的元素排到正确的位置.它只能对整数进行排序. (2)算法描述和实现 得到待排序数的范围(在

排序算法下——桶排序、计数排序和基数排序

桶排序.计数排序和基数排序这三种算法的时间复杂度都为 $O(n)$,因此,它们也被叫作线性排序(Linear Sort).之所以能做到线性,是因为这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作. 1. 桶排序(Bucket Sort)? 1.1. 桶排序原理 桶排序,顾名思义,要用到"桶".核心思想是将要排序的数据分到几个有序的桶里,每个桶的数据再单独进行排序.桶内排完序后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了. 1.2. 桶排序的时间复杂度分析 如

常见排序算法之计数排序与基数排序

1.计数排序 顾名思义,是对待排序数组中的数据进行统计,然后根据统计的数据进行排序,例如: 待排序数组: a[] = { 100, 123, 112, 123, 201, 123, 112, 156, 156, 178, 185, 156, 123, 112 } 首先取出数组中最大值与最小值(这个不用说了吧) 最大值:201  最小值:100 因此该数组的数值范围为 201 - 100 + 1 = 102 开辟一个大小为102的数组 count[102] 并将所有元素初始化为0 再次遍历数组,以

牺牲空间换时间的非比较排序之计数排序和基数排序

非比较排序试用于元素比较集中的序列. 1.计数排序 找出待排序的数组中最大和最小的元素 统计数组中每个值为i的元素出现的次数,存入数组C的第i项 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1 void CountSort(int *a, int size) { assert(a); int max = a[0]; int min = a[0]; for (int i = 1; i < siz

线性排序:计数排序与基数排序

前面讨论了几种排序算法,三种O(n2)时间复杂度的排序算法:插入,选择和冒泡和两种O(nlgn)的算法:快速排序和归并排序.这几种排序数组除了归并排序需要额外的数组开销.其他几个的空间复杂度都是O(1).通过比较交换元素完成排序. 计数排序是利用空间换取时间,增加了两个额外数组的开销,而且计数排序有一定限制:即待排数组元素的大小在0-P之间.如果P的值很大,那么额外数组的开销也会很大. 计数排序,顾名思义就是通过一个额外数组来记下待排序数组中元素出现的次数. void countsort(int

计数排序and基数排序

1 计数排序,稳定    复杂度o(k + n) public static int[] countingSort(int[] nums) { int n = nums.length; int k = 0; for (int i = 0; i < n; i++) { k = Math.max(k, nums[i]); } int[] count = new int[k + 1]; int[] res = new int[n]; for (int i = 0; i < n; i++) { cou