排序(补充):计数排序,基数排序(详细到逆天)

目录

计数排序

基数排序

计数排序

现在假如说我给一个数组{3,4,1,7,7,5,9,6,2}来进行排序,我们先找到数组中最大的值max,开辟max+1(以0开始)的辅助空间,里面存放0,如下图

然后我们遍历数组a[],将数组a[]里的元素在help下标对应的位置,让这个下标对应的位置上的值+1

我们在help填入值后是这样的,然后我们需要遍历help中不为0的位置,填入a中覆盖原来的值

#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
int main()
{
	int a[] = { 8,5,6,20,7,16,18,13 };
	int n = sizeof(a) / sizeof(int) - 1;
	int maxn = *max_element(a,a+n);
	int* cul = (int*)malloc(sizeof(int) * maxn);
	for (int i = 0; i <= maxn; i++)
	{
		cul[i] = 0;
	}
	for (int i = 0; i <= n; i++)
	{
		cul[a[i]]++;
	}
	int k = 0;
	for (int i = 0; i <= maxn; i++)
	{
		if (cul[i] != 0)
		{
			for (int j = cul[i]; j > 0; j--)
			{
				a[k] = i;
				k++;
			}
		}
	}
	for (int i = 0; i <= n; i++)
	{
		cout << a[i] << " ";
	}
	return 0;
}

基数排序

我们先说说什么是基数:一个数值的各个位上的数字称为该位的基数,128的个位基数是8,十位基数是2,百位基数是1

基数排序的思路:假如我们要对{2,5,20,5,678,128,123}进行排序,我们先对个位上的数进行排序,在个位上有序的基础上对十位上的数进行排序,在此基础上对百位数上的数进行排序,最后得出来的数组就是有序的了,示意图如下

#include<iostream>
#include<vector>
using namespace std;
int maxwei(vector<int>input)//找到vector容器中最大值的位数
{
	int max_num = input[0];
	for (int i = 0; i < input.size(); i++)
	{
		if (input[i] > max_num)
		{
			max_num = input[i];
		}
	}
	int p = 0;
	while (max_num > 0)
	{
		p++;
		max_num /= 10;
	}
	return p;
}
int GetNum(int num, int d)//取出数的第d位上的数字
{
	int p = 1;
	while (d - 1 > 0)
	{
		p *= 10;
		d--;
	}
	return num / p % 10;
}
vector <int> RadixSort(vector<int> input, int length)//基数排序
{
	vector<int>bucket(length);//创建临时存放排序过程中的数据
	vector<int>count(10);

	for (int d = 1; d <= maxwei(input); d++)
	{
		//计数器清0
		for (int i = 0; i < 10; i++)
		{
			count[i] = 0;
		}

		//统计各个桶中的个数
		for (int i = 0; i < length; i++)
		{
			count[GetNum(input[i], d)]++;
		}
		//得到的每个数应该放到bucket中的位置
		for (int i = 1; i < 10; i++)
		{
			count[i] += count[i - 1];
		}

		for (int i = length - 1; i >= 0; i--)
     //特别注意这一步,下面会有详细说明,为什么要这样逆序遍历
		{
			int k = GetNum(input[i], d);
			bucket[count[k] - 1] = input[i];
			count[k]--;
		}
		for (int j = 0; j < length; j++)
		{
			input[j] = bucket[j];
		}
	}
	return input;
}
int main()
{
	int a[] = { 258,6,3,1,69,7,35,1024,4,0 };
	vector<int>test(a, a + sizeof(a) / sizeof(a[0]));
	cout << "排序前" << ": ";
	for (int i = 0; i < test.size(); i++)
	{
		cout << test[i] << " ";
	}
	cout << endl;

	vector<int>result = test;
	result = RadixSort(result, result.size());
	cout << "排序后" << ": ";
	for (int i = 0; i < result.size(); i++)
	{
		cout << result[i] << " ";
	}
	cout << endl;
	system("pause");
}

我再次强调一下vector <int> RadixSort(vector<int> input, int length)这个基数排序的函数,我们先创建bucket来临时存放数组,假如说我对个位数进行排序后放到这个数组中,再将这里面的数值再赋值给input数组,count容器是用来记录位数的个数,但是记得每进行一次排序之后count值要清零,记录了count之后我怎么去通过count来进行排序呢?我们先将count进行下面的操作

for (int i = 1; i < 10; i++)
        {
            count[i] += count[i - 1];//上面这个图示,为了确定元素的位置
        }

        for (int i = length - 1; i >= 0; i--)//下面有解释
        {
            int k = GetNum(input[i], d);
            bucket[count[k] - 1] = input[i];
            count[k]--;
        }
 

为什么要逆序遍历呢?咱们用一个例子来说[212,213,312],第一次因为是将乱序进行个位排序,逆序对这一次是没啥影响的,但是对以后位数上的排序起着至关重要的作用,我们第一次按照个位排好序之后是[212,312,213],如果我们对十位进行正向遍历之后排序的时候,count中的1会出现3次,我们如果找到一个1之后,bucket[3-1]=input[1],这样bucket[2]=212,然后count[1]减成了2,bucket[2-1] = input[2],bucket[1] = 312,最后bucket[0]=input[3]=213,这样排出来的结果为[213,312,212],怎么我们发现213个位上是3,咋跑到个位上是2的前面了,就很离谱,所以这样是不对的,咱们一定要从后往前遍历,咱们看看从后往前遍历是咋样的!我们第一次排好序和前面是一样的[212,312,213],从后往前遍历,bucket[3-1] = input[2]=213,bucket[2-1]=input[1]=312,bucket[0]=input[0]=212,出来是[212,312,213]这样是正确的,咱们再来看第三回,count计数出来是2的位置上有2个,3的位置上有1个,

那count[2]=2,count[3]=3从后往前遍历的话,先找到input[2]:213,count[2]的位置上是2,南无bucket[2-1]=213,再找到312,bucket[3-1]=312,再找到212,bucket[1-1]=212

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值