转自:http://bbs.ahalei.com/thread-4399-1-1.html
#include <stdio.h> int main() { int a[11],i,j,t; for(i=0;i<=10;i++) a[i]=0; //初始化为0 for(i=1;i<=5;i++) //循环读入5个数 { scanf("%d",&t); //把每一个数读到变量t中 a[t]++; //进行计数 } for(i=0;i<=10;i++) //依次判断a[0]~a[10] for(j=1;j<=a[i];j++) //出现了几次就打印几次 printf("%d ",i); getchar();getchar(); //这里的getchar();用来暂停程序,以便查看程序输出的内容 //也可以用system("pause");等来代替 return 0; }
这种排序方法我们暂且叫他“桶排序”。因为其实真正的桶排序要比这个复杂一些,以后再详细讨论,目前此算法已经能够满足我们的需求了。
这个算法就好比有11个桶,编号从0~10。每出现一个数,就将对应编号的桶中的放一个小旗子,最后只要数数每个桶中有几个小旗子就OK了。例如2号桶中有1个小旗子,表示2出现了一次;3号桶中有1个小旗子,表示3出现了一次;5号桶中有2个小旗子,表示5出现了两次;8号桶中有1个小旗子,表示8出现了一次。
<ignore_js_op>
现在你可以请尝试一下输入n个0~1000之间的整数,将他们从大到小排序。提醒一下如果需要对数据范围在0~1000之间的整数进行排序,我们需要1001个桶,来表示0~1000之间每一个数出现的次数,这一点一定要注意。另外此处的每一个桶的作用其实就是“标记”每个数出现的次数,因此我喜欢将之前的数组a换个更贴切的名字book(book这个单词有记录、标记的意思),代码实现如下。
1 #include <stdio.h> 2 int main() 3 { 4 int book[1001],i,j,t,n; 5 for(i=0;i<=1000;i++) 6 book[i]=0; 7 scanf("%d",&n);//输入一个数n,表示接下来有n个数 8 for(i=1;i<=n;i++)//循环读入n个数,并进行桶排序 9 { 10 scanf("%d",&t); //把每一个数读到变量t中 11 book[t]++; //进行计数,对编号为t的桶放一个小旗子 12 } 13 for(i=1000;i>=0;i--) //依次判断编号1000~0的桶 14 for(j=1;j<=book[i];j++) //出现了几次就将桶的编号打印几次 15 printf("%d ",i); 16 17 getchar();getchar(); 18 return 0; 19 }
最后来说下时间复杂度的问题。代码中第6行的循环一共循环了m次(m为桶的个数),第9行的代码循环了n次(n为待排序数的个数),第14和15行一共循环了m+n次。所以整个排序算法一共执行了m+n+m+n次。我们用大写字母O来表示时间复杂度,因此该算法的时间复杂度是O(m+n+m+n)即O(2*(m+n))。我们在说时间复杂度时候可以忽略较小的常数,最终桶排序的时间复杂度为O(m+n)。还有一点,在表示时间复杂度的时候,n和m通常用大写字母即O(M+N)。
这是一个非常快的排序算法。桶排序从1956年就开始被使用,该算法的基本思想是由E.J.Issac和R.C.Singleton提出来。之前我有说过,其实这并不是真正的桶排序算法,真正的桶排序算法要比这个更加复杂。但是考虑到此处是算法讲解的第一篇,我想还是越简单易懂越好,真正的桶排序留在以后再聊吧。需要说明一点的是:我们目前学习的简化版桶排序算法其本质上还不能算是一个真正意义上的排序算法。为什么呢?例如遇到下面这个例子就没辙了。
现在分别有5个人的名字和分数:huhu 5分、haha 3分、xixi 5分、hengheng 2分和gaoshou 8分。请按照分数从高到低,输出他们的名字。即应该输出gaoshou、huhu、xixi、haha、hengheng。发现问题了没有?如果使用我们刚才简化版的桶排序算法仅仅是把分数进行了排序。最终输出的也仅仅是分数,但没有对人本身进行排序。也就是说,我们现在并不知道排序后的分数原本对应着哪一个人!这该怎么办呢?不要着急请看下节——冒泡排序。