1.桶排序
桶排序从字面上来理解就与桶子有关,而桶子的作用就是装东西,用个题目来理解。
例1:将0到1000中的n个数按从小到大排列(要求多组输入)。
第一行输入n,表示n个数
10
第二行输入要排列的数字
45 556 12 2 548 0 74 56 956 20
思路:桶排序就是将数字放入特定的桶子里面,按照桶子的顺序输出。
像这一道题要排列n个数,而且数字要1000以内。定义一个数组当作桶子,数组可得下标要可以到1000,数组要初始为0。当输入一个数字时,以这个数字为下标的数组就加1,就像念到一个数字就往贴有这个数字标签的桶子中放入一个小球。
输出时,就从小到大将装有小球的桶子上的标签输出,即将不为0的数组的下标输出。同时还要注意有的数字不止有一个的情况,要循环输出。
//桶排序
#include <stdio.h>
int main() {
int n, i, j, x;
int sz[1001] = {0}; //定义一个数组且初始化为0;
while (scanf("%d", &n) != EOF) {
for (i = 1; i <= n; i++) {
scanf("%d", &x); //输入n个数字
sz[x]++; //将输入的数作为数组下标
}
for (i = 1; i <= 1000; i++) { //从小到大找到输入的数
for (j = 1; j <= sz[i]; j++) //同一个数字的次数
printf("%d ", i);
}
}
}
基本原理 对号入座
缺点: 浪费空间
2.快速排序
快速排序最重要的是要找一个基准数(即一个用作参考的数)
例2:对十个整型数进行排序(从小到大)
第一行输入十个整数
6 1 5 44 9 86 2 3 9 20
思路:对于快速排序,要用到二分的思想。 像这题,找到一个基准数,这里就直接用第一个数6作为基准数了。我们需要根据基准数,将数分为两份,一份小于6,另一份大于6。把这两份数分别放于6的两侧,具体放于哪一侧需要参考我们的目的,是从小到大,还是从大到小。 这里是从小到大,比基准数6小的就放在6的左边,大的就放在基准数的右边。为了方便,就分别从最左和最右开始,将数与基准数进行比较。
先定义一个 i 放在数组最左边,一个 j 放在数组的最右边。
之后,让 i 和 j 分别从最左和最右开始移动,一定要让 j 先开始移动( j - -),一直到 j 所在的数字小于基准数6停止,然后 i 再行动( i++)一直到 i 所在的数字大于6停止,交换 i 所在的数和 j 所在的数。
同样的是 j 先动,i 再动,停下的条件不变。
然后继续,直到 i 与 j 相遇,这时就要将 i 和 j 所在位置与基准数位置交换,第一轮排序结束。
第二轮和第一轮一样,只是序列变成了以6为分界的左右两列,再对两个序列重复上述步骤 依旧以每个序列的第一个数为基准数,同样定义个 i 和 j 。记住一定要先从与基准数相反的方向开始。
这是第二趟,之后又分成了几个序列,同样的方法,直到完全排序好。
#include <stdio.h>
int num[101];//定义一个全局变量,子函数和main函数中都可以使用
//定义函数进行快速排序(从小到大)
void quick(int left, int right) {
int i, j;
int base= num[left];//基准数
if (left > right)
return;
i = left;
j = right;
while (i != j) {
//先从右到左
while ( num[j] >= base && i < j)
j--;
//再从左到右
while (num[i] <= base && i < j)
i++;
//交换i和j所在位置的数,这时i和j还没相等
if (i < j) {
int temp = num[i];
num[i] = num[j];
num[j] = temp;
}
}
//直到i与j相等,将其位置的数与基准数交换
num[left] = num[i];
num[i] = base;
//之后又有序列,对其重复上述步骤
//这里i和j已经相等
quick(left, i - 1); //对基准数左边的序列,这是递归过程
quick(i + 1, right); //对基准数右边的序列
return;
}
int main() {
for (int i = 1; i <= 10; i++)
scanf("%d", &num[i]);
quick(1, 10); //调用快速排序函数
//输出
printf("排列好的数组:");
for (int i = 1; i <= 10; i++)
printf("%d ", num[i]);
return 0;
}
注意:为什么要从基准数相反的方向开始呢? 就如上面排雷 2 1 5 3,基准数为2。如果我们先从左边开始,i 一直到5停止,而 j 也是到5停止,这样就只有交换2 和 5 的位置了,并不能达到排序的目的。
如果从右边开始的话,就不会有这种情况。
3.冒泡排序
冒泡就是气泡在水中一样向上冒出,数字在数组中向后移动。
例3:将n个整型数按从大到小排列
第一行输入n,表示n个数 第二行输入要排序的数
5
56 812 421 2 2545
思路: 这5个数字需要一个数组储存,先将这5个数按从大到小排序,就是说数字越小,位置就越靠后。冒泡排序要我们先将最前面的两个数字进行比较,数字小的就后移。
56与812比较,56小,位置后移;56与421比较,56小,位置后移;56与2比较,2小,位置不变;
2与2545比较,2小位置后移。这样最小的2,就到了数组的最后了。
排列后的数组就是 812 421 56 2545 2。这还只是第一趟。第二趟的方法跟刚刚一样,相邻的两个数字进行比较,排列出来是 812 421 2545 56 2;一共有五个数字,所以要进行四趟比较的循环。最后结果为:2545 812 421 56 2。
而要将数组按从小到大的顺序排列 的话,只要比较相邻两个数字谁大,将大的后移就行。
#include <stdio.h>
void big(int q, int x[10001]);
int main() {
int a[10001];
int n;
scanf("%d", &n); //输入n,表n个数
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
big(n, a);
}
//从大到小
void big(int q, int x[10001]) {
for (int i = 1; i <= q - 1; i++) { //需要循环的趟数
for (int j = 1; j <= q - i; j++) { //每一趟要两两对比的次数
if (x[j] < x[j + 1]) {
int temp = x[j];
x[j] = x[j + 1];
x[j + 1] = temp;
}
}
}
printf("排列好的数:");
for (int i = 1; i <= q; i++)
printf("%d ", x[i]);
}
基本原理:双重嵌套循环,两两对比
缺点:对于较多的数,时间复杂度高