排序
1.桶排序
思路:定义一个数组用来存储数值,那个数出现了就将对应的数组元素加一,表示这个数出现过了,出现了几次
#include<stdio.h>
int main(){
int a[11],i,j,t;
for(i=0;i<=10;i++)//实现的是从小到大排序
//如果要求从大到小排序此行可改成for(i=10;i>=0;i--)
a[i]=0;//初始化数组
for(i=1;i<=5;i++){//循环读入五个数
scanf("%d",&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;
}
输入的数据是5 3 5 2 8
现在需要对数据范围在0-1000的整数进行排序,则需要1001个桶,来表示0-1000之间每一个数出现的次数。
#include<stdio.h>
int main(){
int book[1001],i,j,t,n;
for(i=0;i<=1000;i++)
book[i]=0;
scanf("%d",&n);//输入一个数n,表示接下来有n个数
for(i=1;i<=n;i++)//循环读入n个数,并进行排序
{
scanf("%d",&t);//把每一个数读到变量中
book[t]++;//进行计数
}
for(i=1000;i>=0;i--)//以次判断编号1000-0的桶
for(j=1;j<=book[i];j++)//出现了几次就将桶的编号打印几次
printf("%d",i);
getchar();getchar();
return 0;
}
输入
10
8 100 50 22 15 6 1 1000 999 0
运行结果
1000 999 100 50 22 15 8 6 1 0
时间复杂度为O(M+N)
缺陷:只能对分数进行排序,无法将排序后的分数与人对应起来;浪费空间
2.冒泡排序
#include<stdio.h>
int main(){
int a[100]i,j,t,n;
scanf("%d",&n);//输入一个数n,表示接下来有n个数
for(i==1;i<=n;i++)//循环读入n个数到数组a中
scanf("%d",&a[i]);
for(i=1;i<=n-1;i++){//n个数排序,只用进行n-1趟
for(j=1;j<=n-i;j++){//从第一位开始比较直到最后一个尚未归位的数
if(a[j]<a[j+1])//比较大小并交换
{t=a[j];a[j]=a[j+1];a[j+1]=t}
}
}
for(i=1;i<=n;i++)//输出结果
printf("%d",a[i]);
getchar();getchar();
return 0;
}
输入
10
8 100 50 22 15 6 1 1000 999 0
运行结果
0 1 6 8 15 22 50 100 999 1000
稍加修改解决第一节留下来的问题
#include<stdio.h>
struct student
{
char name[21];
char score;
};//创建一个结构体来存储姓名和分数
int main(){
struct studentt a[100],t;
int i,j,n;
scanf("%d",&n);//输入一个数n
for(i=1;i<=n;i++)//循环读入n个人名和分数
scanf("%s %d",a[i].name,&a[i].score);
//按分数从高到低进行排序
for(i=1;i<=n-1;i++)
{
for(j=1;j<=n-i;j++)
{
if(a[j].score<a[j+1].score)//分数进行比较
{t=a[j];a[j]=a[j+1];a[j+1]=t}
}
}
for(i=1;i<=n;i++)//输出人名
printf("%s\n",a[i].name);
getchar();getchar();
return 0;
}
输入
5
huhu 5
haha 3
xixi 5
hengheng 2
gaoshou 8
运行结果
gaoshou
huhu
xixi
haha
hengheng
冒泡排序的核心是双重嵌套循环,其时间复杂度是O(N*N)
3.快速排序
#include<stdio.h>
int a[101],n;//定义全局变量,这两个变量需要在子函数中使用
void quicksort(int left,int right)
{
int i,j,t,temp;
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[i];
a[i]=a[j];
a[j]=t;
}
}
//最终将基准数归位
a[left]=a[i];
a[i]=temp;
quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程
quicksort(i+1,right);
}
int main(){
int i,j,t;
//读入数据
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
quicksort(1,n);//快速排序调用
//输出排序后的结果
for(i=1;i<=n;i++)
printf("%d",a[i]);
getchar();getchar();
return 0;
}
输入
10
6 1 2 7 9 3 4 5 10 8
运行结果
1 2 3 4 5 6 7 8 9 10
快速排序的时间复杂度和冒泡排序是一样的,都是O(N*N),它的平均时间复杂度是O(NlogN)。快速排序是基于一种二分的思想。
例题:小亨买书
输入有两行,第一行为一个正整数,表示有n个同学参与调查(n<=100),第二行有n个用空格隔开的正整数,为每本图书的ISBN号(假设图书的ISBN号在1-1000)
输出也是两行,第一行为一个正整数k,表示需要买多少本书。第二行为k个用空格隔开的正整数,为从小到大已排序好的需要购买的图书的ISBN号。
例如输入
10
20 40 32 67 40 20 89 300 400 15
则输出
8
15 20 32 40 67 89 300 400
解决这个问题有两种方法:
①先将这n个图书的ISBN号去重,再进行从小到大排序并输出
②先从小到大排序,输出的时候再去重
方法一:
对桶排序稍加改造正好可以起到去重作用
这个的时间复杂度为O(M+N)
#include<stdio.h>
int main(){
int a[1001],i,n,t;
for(i=1;i<=1000;i++)
a[i]=0;//初始化
scanf("%d",&n);
for(i=1;i<=n;i++)//循环读入n个图书的ISBN号
{
scanf("%d",&t);//把每一个ISBN号读到变量t中年
a[t]=1;//标记出现过的ISBN号
}
for(i=1;i<=1000;i++)//依次判断1-1000这个1000个桶
{
if(a[i]==1)//如果这个ISBN号出现过则打印出来
printf("%d",i);
}
getchar();getchar();
return 0;
}
方法二:
先排序再去重。因为已经排好序了,所以相同的数都会紧挨在一起,只要在输出的时候,预先判断一下当前这个数与前面一个数是否相同。相同则表示这个数之前已经输出过了,不用再次输出。不同则表示这个数是第一次出现,需要输出这个数
这种方法的时间复杂度由两部分组成,一部分是冒泡排序的时间复杂度,是N(NN),另一部分是读入和输出,都是O(N),因此整个算法的时间复杂度是O(2N+NN),相对于NN来说,2N可以忽略,最终该方法的时间复杂度是O(NN)
#include<stdio.h>
int main()
{
int a[101],n,i,j,t;
scanf("%d",&n);//读入n
for(i=1;i<=n;i++)//循环读入n个图书的ISBN号
{
scanf("%d",&a[i]);
}
//开始冒泡排序
for(i=1;i<=n-1;i++)
{
for(j=1;j<=n-i;j++)
{
if(a[j]>a[j+1])
{t=a[j];a[j]=a[j+1];a[j+1]=t}
}
}
printf("%d",a[1]);//输出第一个数
for(i=2;i<=n;i++)//从2循环到n
{
if(a[i] !=a[i-1])
printf("%d",a[i]);
}
getchar();getchar();
return 0;
}