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

一、计数排序

稳定、 当输入的元素是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[], int B[], int n)
{
	int *C = new int[K];
	int i, j;

	//初始化
	memset(C, 0, sizeof(int)*K);

	//统计
	for (i = 0; i < n; i++)
		C[A[i]]++;	//C[i] = |{key=i}|

	for (i = 1; i < K; i++)
		C[i] += C[i - 1];	//C[i] = |{key <= i}|
	//分配
	for(j = n - 1; j >= 0; j--)
	//for(j = 0; j < n; j++)
	{
		//B[C[A[j]] - 1] = A[j];
		//C[A[j]]--;

		B[--C[A[j]]] = A[j];	 //对于每一个A[j]来说,C[A[j]]-1就是A[j]在输出数组中的最终位置,因为共有C[A[j]]个元素小于或等于A[j]
	}

	delete []C;
}

参考算法导论p108~110。

用于年龄排序(剑指offer—p65):

void SortAges(int ages[], int length)
{
	if (ages == NULL || length <= 0)
		return;
	const int oldestAge = 99;
	int timeOfAges[oldestAge + 1];
	memset(timeOfAges, 0, sizeof(timeOfAges));
	for (int i = 0; i < length; i++)
	{
		int age = ages[i];
		if (age < 0 || age > oldestAge)
			throw new std::exception("age out of range.");
		timeOfAges[age]++;
	}
	int index = 0;
	for (int i = 0; i <= oldestAge; i++)
	{
		for (int j = 0; j < timeOfAges[i]; j++)
		{
			ages[index++] = i;
		}
	}
}

上述两个函数最后部分不同,但功能相同。



可以利用计数排序的思想,在O(1)的时间复杂度内计算介于0~k之间的n个整数中有多少个落在区间[a,b]内(算法导论p110题8.2-4):

int CountNumberBetweenAB(int L[], int n, int a, int b)
{
	const int K = 100;
	int *c = new int[K];
	int i;

	memset(c, 0, sizeof(int)*K);
	for (i = 0; i < n; i++)
		c[L[i]]++;
	for (i = 1; i < K; i++)
		c[i] += c[i - 1];

	int count = (a == 0 ? c[b] : c[b] - c[a - 1]);
	delete c;

	return count;
}

二、基数排序

稳定、按最低有效位到最高有效位进行排序(中间采用的是稳定呢的计数排序), 当输入的元素是n 个d位整数时,它的运行时间是 O(d(n + k)),k一般是10,空间复杂度是O(n)

void RadixSort(int A[], int n, int d)
{
	const int K = 10;
	int i, j;
	int *digitI = new int[n], c[K];
	int *tmp = new int[n];//临时数组
	int base = 1;

	for (i = 0; i < d; i++)
	{
		memset(c, 0, sizeof(int)*K);
		for (j = 0; j < n; j++)
		{
			digitI[j] = (A[j] / base) % K;	  //从低位到高位取每一位
			c[digitI[j]]++;
			//printf("%3d ", digitI[j]);
		}
		//cout << endl;
		for (j = 1; j < K; j++)
			c[j] += c[j - 1];

		for (j = n - 1; j >= 0; j--)
			tmp[--c[digitI[j]]] = A[j];   // = digitI[j]]

		//for (j = 0; j < n; j++)
		//	printf("%03d ", tmp[j]);
		//cout << endl;

		memcpy(A, tmp, sizeof(int)*n);
		base *= 10;
	}
	memcpy(A, tmp, sizeof(int)*n);

	delete []tmp;
	delete []digitI;
}

参考算法导论p110~112

三、桶排序

桶排序假设输入是由一个随机过程产生,该过程将元素均匀、独立地分布在[0,1)区间上。桶排序是稳定的、期望时间复杂度是O(n)空间复杂度是O(k*n/k) = O(n)

/*initial arr*/  
void InitialArr(double *arr,int n)  
{  
	srand((unsigned)time(NULL));  
	for (int i = 0; i < n;i++)  
	{  
		arr[i] = rand() / double(RAND_MAX+1);   //[0.1)  
	}  
}
void BucketSort(double arr[], int n)
{
	const int K = 10;//桶的个数
	int i, j, k;  

	double *bucket[K];
	for (i = 0; i < K; i++)
		bucket[i] = new double[n];

	int count[K] = {0};//每一个桶中的元素个数

	for (i = 0; i < n; i++)
	{
		double tmp = arr[i];
		int digitI = (int)(tmp*10);	//digitI表示第i个元素的小数点第一位
						//小数点第一位(digitI)相同的元素放在同一个桶(bucket[digitI])中的第count[digitI]个位置上
		                                //通过插入排序将tmp插入到有序的bucket[digitI][0,...,count[digitI] - 1]
		for (j = count[digitI] - 1; j >= 0 && tmp < bucket[digitI][j]; j--)
			bucket[digitI][j + 1] = bucket[digitI][j];
		bucket[digitI][j + 1] = tmp;
		count[digitI]++;
	}

	//重新连接数据
	k = 0;
	for (i = 0; i < K; i++)
		for (j = 0; j < count[i]; j++)
			arr[k++] = bucket[i][j];

   for (i = 0; i < K; i++)
	   delete []bucket[i];
}

参考算法导论p112~114

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值