二话不说,先上代码:
接下来将重点解释有important注释的地方:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000005;
long long origin[N],final[N],last[N];//原始数组,当前数组,原始数组当前的关键字
int bucket[12];//桶
int cout=15;//位数,最大位数
int po(int a)//乘方函数
{
int n=1;
for(int i=1;i<a;++i)
{
n*=10;
}
return n;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&origin[i]);
}
for(int i=0;i<=cout;++i)
{
memset(bucket,0,sizeof(bucket));
for(int j=1;j<=n;++j)//取出马上要排序的关键字,一般从个位开始往上取。
{
last[j]=(origin[j]/po(i))%10;
bucket[last[j]]++;//桶排操作
}
for(int j=1;j<=9;++j)//求前缀和操作,就是看当前关键字,可以大概由小到大排到多少位,方便下面对最终数组进行排序!
{
bucket[j]+=bucket[j-1];//☣
}
for(int j=n;j>=1;--j)//important
{
final[bucket[last[j]]]=origin[j];
bucket[last[j]]--;//important
}
for(int j=1;j<=n;++j)//赋值操作
{
origin[j]=final[j];
}
}
for(int i=1;i<=n;++i)//输出
{
printf("%d ",final[i]);
}
return 0;
}
P.s:前缀和指当前所求关键字,加上自己同类的干扰(关键字一样)由小到大排到的位置(排到自己同类的最后面,最坏情况)。
重点来了!!!
important就是重点,首先我们可以发现,这里的for循环和前面的for循环有点不太一样,这个我们稍后作解释,我们先来解释一下下面的三重括号,最里面一重是当前原来数组(origin)的最后一位数;第二重就是这个最后一位数所在的桶的大概位置(前面求了前缀和,前缀和概念见前面P.s)这就是他final的位置,这个时候为了防止对后面一样的关键字有影响(两个关键字排在同一位置),所以对他的前缀和进行--操作,反正考虑的是最坏情况,也就是说,一来初始的前缀和一定是当前关键字所在桶的最后一位,所以我们进行--操作最多也就是减到第一位,并不会影响到前面一个桶,(若他是第一个桶,我相信大家照这个理解会更容易一些!)。
相信大家上面都懂了,现在我们来讲一讲细节!!!
为什么上面的for循环要反着写?
为了使我们的解释更加方便,我们在这里为大家造一组数据:
32 31 23 24
你可能会说,看上去貌似没有什么问题啊? 别急!!
首先我们先进行第一波排序,这一波以个位为关键字:
结果如下:
31 32 23 24
正常,对吧!
再来,重点来了,大家可以仔细品读下面这段话!
第二波因为是从前往后对关键字为十位来进行排序,那我们慢慢来:
1、首先23,24会被丢进二号桶,31,32会被扔进3号桶,所以2,3号桶各两个量,3号桶的前缀和为4,2号桶为2.
_2、下面进行从前往后遍历:
_
(1)首先,31,3号桶,前缀和4,扔入final数组4号位
(2)其次,32,3号桶,前缀和4--=3,扔入final数组3号位(细心的朋友应该已经发现问题了!)
(3)再其次,23,2号桶,前缀和2,扔入final数组2号位
(4)最后,24,2号桶,前缀和2--=1,扔入final数组1号位( 又出现了同样的问题,哈哈!)
_3_最后的扯皮答案:
24 23 32 31
大家都应该知道问题在哪里了吧!因为我们要遵守优先级规则,后面的origin数组中低位一定比高位大,所以我们必须从后面开始排,否则会出现如果高位关键字的大小是一样的,那么考前面低位更小的排入final数组中反而会占据优先级更大的位置(也就是说31会在32后面!!!)
如果你是一个皮怪,“我就想这样写!!!”,你可以这样做,首先优先级变为前面所有桶的大小+1(这一步忒麻烦,还要多定一个数组,唉!),然后后面的for循环你就可以正着写,最后把--改为++;
以上就是我对这道题的所有解读,希望可以帮助到你!!!,谢谢采纳!