排序算法之非比较排序【计数排序和基数排序】

------------------------------------------------------- 计数排序---------------------------------------------------------------

简而言之就是统计元素出现的个数,并将其放在相应的桶中,这是一种牺牲时间换空间的做法,但是在某种意义上还是特别快的

【图解】


【性能分析】

时间复杂度:设数据有N个,第一遍统计字数据出现次数,遍历原数据,复杂度为O(N),第二遍遍历哈希表,向原空间写数据,遍历了范围次(range),时间复杂度为O(range),所以总的时间复杂度为O(N+range)。

空间复杂度:O(range)。

稳定性:从上面的例子中可以看出,该算法是稳定的。

【代码实现】

void CountSort(int *arr, size_t sz)
{
	int min = arr[0];
	int max = arr[0];
	for (size_t i = 0; i < sz; i++)//先确定开辟的空间大小
	{
		if (arr[i] < min)
			min = arr[i];
		if (arr[i]>max)
			max = arr[i];
	}
	int range = max - min + 1;//确定好范围
	int *a = new int[range];//开辟空间
	memset(a, 0, sizeof(int)*range);//初始化为0
	for (size_t i = 0; i < sz; i++)//统计数据个数
	{
		a[arr[i] - min]++;//每个数据放在相对位置
	}
	size_t j = 0;
	for (size_t i = 0; i < range; i++)
	{
		while (a[i]--)
		{
			arr[j++] = i + min;
		}
	}
	delete[] a;
}

测试代码:

void Print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr1[] = {105,110,107,103,104,109,105,106,};
	Print(arr1, sizeof(arr1) / sizeof(arr1[0]));
	CountSort(arr1, sizeof(arr1) / sizeof(arr1[0]));
	Print(arr1,sizeof(arr1) / sizeof(arr1[0]));
	getchar();
	return 0;
}

----------------------------------------------------基数排序---------------------------------------------------------------------

基数排序属于”分配式排序“,又称“桶子法”或者bit sort,顾名思义:他是透过键值将要排序的元素分配到某些“桶”中,已达到排序的作用。

实现方法

最高位优先:简称MSD法;先按k1排序分组,同一组中记录,关键码k1相等。再对各组按k2排序分组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各组排序后。再将各组连接,便得到一个有序序列

最低位优先:简称LSD法;先从kd开始排序,再对kd-1进行排序,依法重复,直到对k1排序后便得到一个有序序列

【性能分析】


代码实现

//基数排序
//LSD--->优先个位
void LSDSort(int *arr, size_t sz)
{
	int digits = 1;//统计排序数据是几位数
	/*for (size_t i = 0; i < sz; i++)
	{
	while (arr[i] / 10)
	{
	++digits;
	}
	}*/
	//以下做法的优势:没有使用除法(除法效率最低)
	int base = 10;
	for (size_t i = 0; i < sz; i++)
	{
		while (arr[i] >= base)
		{
			++digits;
			base *= 10;
		}
	}
	base = 1;
	int* Buckets = new int[sz];
	for (size_t i = 0; i < digits; i++)
	{
		//统计每个桶里面的个数(设置0-9个桶)
		int counts[10] = { 0 };
		for (size_t i = 0; i < sz; i++)
		{
			int num = (arr[i] / base ) % 10;
			counts[num]++;
		}
		//每个桶中第一个数据的起始位置
		int starts[10] = { 0 };
		for (size_t i = 1; i < 10; i++)
		{
			starts[i] = starts[i - 1] + counts[i - 1];
		}
		//快速定位值
		for (size_t i = 0; i < sz; i++)
		{
			//int num = arr[i] % 10;//确定在哪个桶中
			//int index = starts[num];
			//Buckets[index] = arr[i];
			//starts[num]++;
			int num = (arr[i]/base) %10;//确定在哪个桶中
			int& index = starts[num];//确定起始位置
			Buckets[index] = arr[i];
			++index;
		}
		memcpy(arr, Buckets, sizeof(int)*sz);
		base *= 10;
	}
	delete[] Buckets;
}
void MSDSort(int* arr, size_t sz)
{
	int digits = 1;//统计排序数据是几位数
	int base = 10;
	for (size_t i = 0; i < sz; i++)
	{
		while (arr[i] >= base)
		{
			++digits;
			base *= 10;
		}
	}
	base = (int)pow((double)10,digits-1);
	int* Buckets = new int[sz];
	for (size_t i = 0; i < digits; i++)
	{
		//统计每个桶里面的个数(设置0-9个桶)
		int counts[10] = { 0 };
		for (size_t i = 0; i < sz; i++)
		{
			int num = (arr[i] / base) % 10;
			counts[num]++;
		}
		//每个桶中第一个数据的起始位置
		int starts[10] = { 0 };
		for (size_t i = 1; i < 10; i++)
		{
			starts[i] = starts[i - 1] + counts[i - 1];
		}
		//快速定位值(将各元素放置到对应的桶中)
		for (size_t i = 0; i < sz; i++)
		{
			//int num = arr[i] % 10;//确定在哪个桶中
			//int index = starts[num];
			//Buckets[index] = arr[i];
			//starts[num]++;
			int num = (arr[i] / base) % 10;//确定在哪个桶中
			int& index = starts[num];//确定起始位置
			Buckets[index] = arr[i];
			++index;
		}
		memcpy(arr, Buckets, sizeof(int)*sz);
		digits--;
	}
	delete[] Buckets;
}

测试代码

void Print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr1[] = { 173, 222, 493, 543, 155, 614, 228, 465, 339, 881 };
	Print(arr1, sizeof(arr1) / sizeof(arr1[0]));
	LSDSort(arr1, sizeof(arr1) / sizeof(arr1[0]));
	//MSDSort(arr1, sizeof(arr1) / sizeof(arr1[0]));
	Print(arr1,sizeof(arr1) / sizeof(arr1[0]));
	getchar();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值