最快最简单的排序---桶排序
简化版桶排序
例题:期末考试完了,老师要同学们的分数按照从高到低的顺序排序,班上只有五个同学,分别得分5、3、5、2、8分(满分10分)。
思想:
1. 我们需要申请一个大小为11的数组a[11],编号从a[0]~a[10]。刚开始时,我们把这11个变量全赋值为0,表示这些分数还都没有人得过。例如,a[0]=0表示目前还没有人得过0分;同理a[1]=0表示还没有人得过1分......
2. 下面开始处理每一个人的分数。第一个人的分数是5分,我们就将相应的a[5]的值在原来基础上增加1,即将a[5]的值从0改为1,表示5分出现过一次。第二个人的分数是3分,我们就将相应的a[3]的值在原来基础上增加1,即将a[3]的值从0改为1,表示3分出现过一次。注意啦,第三个人的分数也是五分,所以a[5]的值需要在1的基础上再增加1,表示5分出现两次。依次类推。
3. 你发现没有,a[0]~a[10]中的数值其实就是0分到10分每个分数出现的次数。接下来我们只需将出现过的分数打印出来就可以了,出现几次就打印几次。
相应代码:
#include<cstdio>
int main() {
int a[11],n;
for (int i = 1; i < 11; i++)
a[i] = 0;//初始化为零
for (int i = 1; i <= 5; i++) {
scanf("%d", &n);//把每一个数读到变量t中
a[n]++;//关键
}
for (int i = 1; i < 11; i++) {//关键
for (int j = 1; j <= a[i]; j++) {//出现了几次就打印几次
printf("%d ", i);
}
}
system("pause");
return 0;
}
代码实现:
例题2:
输入
10
8 100 50 22 15 6 1 1000 999 0
输出
1000 999 100 50 22 15 8 6 1 0
相应代码:
#include<cstdio>
int main() {
int book[maxn], n, m, p;
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < maxn; i++)
book[i] = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &m);
book[m]++;
}
for (int i = maxn - 1; i >= 0; i--)//从大到小输出
for (int j = 1; j <= book[i]; j++)
printf("%d ", i);
printf("\n");
}
system("pause");
return 0;
}
时间复杂度O(n+m)。
这只是一个简单的桶排序算法,真正的通排序算法要比这个更复杂。现在分别有5个人的名字和分数:aa 5、bb 3、cc 5、dd 2、nn 8,要求按照分数从高到低输出他们的名字。此题如果使用简单的桶排序算法仅仅是把分数进行了排序,对人本身没有进行排序。所以,就要运用另一种排序方法---冒泡排序。
邻居好说话---冒泡排序
冒泡排序的基本思想:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
例如需要将12 35 99 18 76这五个数按照从大到小排序,首先比较第一位与第二位的大小,12小于35两者交换,变为35 12 99 18 76;再比较第二位与第三位的大小,12与99比较前者小于后者,两者交换,变为35 99 12 18 76;依次类推,直到12(最小的排到最后)排到最后,第一趟结束,变为35 99 18 76 12。再进行第二趟、第三趟...直到按照从大到小的顺序排好为止。
总结:如果有n个数进行排序,只需将n-1个数归位,也就是说要进行n-1趟操作。
例题:
输入
10
8 100 50 22 15 6 1 1000 999 0
输出
1000 999 100 50 22 15 8 6 1 0
相应代码:
#include<cstdio>
int main() {
int n, a[maxn];
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {//关键一步,减少每一步的重复
int temp;
if (a[j] < a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n\n");
}
system("pause");
return 0;
}
桶排序解决不了的问题:
代码实现:
相应代码:
#include<cstdio>
struct student {
char name[25];
int score;
};//创建一个结构体用来存储姓名和分数
int main() {
struct student a[100], t;
int n,m;
while (scanf("%d", &n) != EOF) {
scanf("%d", &m);
for (int i = 1; i <= m; i++)
scanf("%s%d", &a[i].name, &a[i].score);
//按分数从高到低进行排序
for (int i = 1; i <= m - 1; i++) {
for (int j = 1; j <= m - i; j++) {
if (a[j].score < a[j + 1].score) {//整体互换
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
for (int i = 1; i <= m; i++)
printf("%s\n", a[i].name);
printf("\n");
}
system("pause");
return 0;
}
冒泡排序的核心部分是双重嵌套循环。时间复杂度为O(n^2)。
3. 最常用的排序---快速排序
冒泡排序虽然解决了桶排序浪费空间的问题,但在算法的执行效率上牺牲了很多,而快速排序既不浪费空间又可以快一点。
思想:
首先要在要排序的数列中找一个数作为基准数。例如,对序列6 1 2 7 9 3 4 5 10 8进行排序,可将6作为基准数;然后,通过快速排序法将小于基准数6的数放在6的左边,大于基准数6的数放在6的右边;再运用快速排序函数分别将基准数左右的序列进行排序,知道排好为止。
注意,每次都是从右往左先开始移动,再从左往右移动寻找。
例题:
输入
10
6 1 2 7 9 3 4 5 10 8
输出
1 2 3 4 5 6 7 8 9 10
相应代码:
#include<cstdio>
int a[101], n;
void quicksort(int left, int right) {
int t, temp, i, j;
if (left > right)return;
temp = a[left];//temp中存的是基准数
i = left;
j = right;
while (i != j) {//顺序很重要
while (a[j] >= temp && i < j)//先从右往左找
j--;
while (a[i] <= temp && i < j)//再从左往右找
i++;
//交换两个数在数组中的位置
if (i < j) {//当哨兵i和哨兵j没有相遇时
t = a[j];
a[j] = a[i];
a[i] = t;
}
}
//最终将基准数归位
a[left] = a[i];
a[i] = temp;
quicksort(left, i - 1);//继续处理左边的,这是一个递归的过程
quicksort(i + 1, right);//继续处理右边的,这是一个递归的过程
return;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
quicksort(1, n);
for (int i = 1; i <= n; i++)
printf("%d ", a[i]);
printf("\n");
system("pause");
return 0;
}
关键点是运用了函数的调用和递归。