4.1.1 选择排序(简单选择排序)
4.1.1.1 基本概念:
简单排序是指,对一个序列A中的元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[1,i-1]形成新的有序区间[1,i]。于是在n趟操作后,所有元素就会是有序的。
于是算法实现的逻辑就很明显:总共需要进行n趟操作(1<=i<=n),每趟操作选出待排序部分[i,n]中最小的元素,令其与A[i]交换。总复杂度为O(n^2),代码如下:
void selectSort()
{
for(int i=1; i<=n; i++) //进行n趟操作
{
int k=i;
for(int j=i; j<=n; j++) //选出[i,n]中最小的元素,下标为k
{
if(A[j]<A[k])
{
k=j;
}
}
int temp=A[i]; //交换A[k]与A[i]
A[i]=A[k];
A[k]=temp;
}
}
4.1.2 插入排序(直接插入排序)
4.1.2.1 基本概念:
对序列A的n个元素A[1]~A[n],令i从2到n枚举,进行n-1趟操作。假设某一趟时,序列A的前i-1个元素A[1] ~ A[i-1]已经有序,而范围[i,n]还未有序,那么该趟从范围[1,i-1]中寻找某个位置j,使得将A[i]插入位置位置j后(此时A[j] ~ A[i-1]会后移一位至A[j+1] ~ A[i]),范围[1,i]有序。通过下面例子具体说明:
插入排序是将待插入元素一个个插入初始已有序的部分过程中,而插入位置的选择遵循使插入后任然有序的原则,具体做法是从后往前枚举已有序的部分来确定插入位置,代码如下:
int v[maxn],n //n个元素,下标从1~~n
void insertsort()
{ //由于下标是从1开始,所以如果我们排序的话,让下标从2开始
for(int i=2; i<=n; i++) //循环n-1趟
{
int temp=v[i],j=i;
while(j>1 && temp<v[j-1]) //只要前面的值大于temp,则直接向后移动,此时temp所对应的值是j而他前面的是j-1,j不能移动到最前面的位置
{
v[j]=v[j-1]; //将前面的值放到后面
j--;
}
v[j]=temp; //当上面的while结束时,j已经到达合适位置,所以再把temp给v[j]即可
}
}
例题 pat A1025
单词:
registration 注册
nondecreasing 不减,递增
题意:
输入:
1.输入n,表示有n个测试组
2.输入k,表示有k个学生
3.每次输入一个13位的编号+成绩
输出:
1.输出总人数
2.输出学生编号,总排名,考场位置,数组在考场内的排名
3.排名原则:成绩不同,降序排列;否则,按照编号的升序排列
特殊技巧:排名的实现
分数不同的排名不同,分数相同但占用一个位置,比如5个学生分数分别为90 88 88 88 86 (注意成绩必须是倒叙排列)那么这5个学生排名是1 2 2 2 5
第一种方法:
从数组下标为0开始的排名为1,然后遍历其余部分;如果此人分数与前人相同,则排名不变;否则,排名为数组下标+1。
stu[0].r=1;
for(int i=1; i<n; i++)
{
if(stu[i].score==stu[i-1].score)
stu[i].r=stu[i-1].r
else
stu[i].r=i+1;
}
第二种方法:
有时题目不需要将排名记录下来,而是直接输出。可以令int类型的变量r为1,然后遍历所有个体;如果当前个体不是第一个且当前个体的分数不等于上一个个体的分数,则r=数组下标+1,此时r就是当前个体的排名。这样的做法适用于输出信息过多,导致第一种方法代码冗长。
int r=1;
for(int i=0; i<n; i++)
{
if(i>0 && stu[i].score!=stu[i-1].score)
r=i+1;
cout << r; //或者stu[i].r=r;
}
解题思路:
1.由于要有总排名,考场排名…所以在定义结构时将这些内容加进去
2.cmp排序要求是:成绩不同,按照成绩降序排列,否则按照id的升序排列
3.主函数:
1.输入信息,并在其考场内进行排序,按照书上第1中方法确定location_number和local_rank
2.对整个结构数组排序,然后书上第2中方法,按要求输出
参考代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
/*
解题思路:
1.由于要有总排名,考场排名...所以在定义结构时将这些内容加进去
2.cmp排序要求是:成绩不同,按照成绩降序排列,否则按照id的升序排列
3.主函数:
1.输入信息,并在其考场内进行排序,按照书上第1中方法确定location_number和local_rank
2.对整个结构数组排序,然后书上第2中方法,按要求输出
*/
struct Student
{
char id[15]; //准考证号
int score; //分数
int location_number; //考场号
int local_rank; //考场内排名
}stu [30010]; //n<=100 , k<=300 所以长度设置为100*300+1=30000+
bool cmp(Student a, Student b)
{
if(a.score!=b.score)
return a.score>b.score; //先按照分数从高到低排序
else
return strcmp(a.id,b.id)<0; //分数相同按准考证从小到大排序
}
int main()
{
int n,k,num=0; //num为总考生数
scanf("%d",&n); //n为考场数
for(int i=1; i<=n; i++)
{
scanf("%d",&k); //k就是每次输入的临时变量
for(int j=0; j<k; j++)
{
scanf("%s %d",stu[num].id,&stu[num].score); //第一变量不加入&因为结构中id就表示地址
stu[num].location_number=i; //该考生的考场号为i
num++; //第一次循环完之后,num的值为5;第2次循环完之后num为9
}
sort(stu+num-k, stu+num, cmp); //将该考场的考生排序
stu[num-k].local_rank=1; //该考场第1名的local_rank即为1
for(int j=num-k+1; j<num; j++)
{//对该考场剩余的考生
if(stu[j].score==stu[j-1].score)
{
stu[j].local_rank=stu[j-1].local_rank;
}
else
{
//local_rank为该考生前的人数
stu[j].local_rank=j+1-(num-k);
}
}
}
printf("%d\n",num); //输出总考生人数
sort(stu,stu+num,cmp); //将所有考生排序
int r=1;
for(int i=0; i<num; i++)
{
if(i>0 && stu[i].score!=stu[i-1].score)
{
r=i+1;
}
printf("%s ",stu[i].id);
printf("%d %d %d\n",r,stu[i].location_number,stu[i].local_rank);
}
return 0;
}
注意事项:
1.student结构中,字符数组char id[15],在用scanf输入时,不用在前面加取址符&
scanf("%s %d",stu[num].id,&stu[num].score);
- 用sort来对普通数组进行排序
sort(stu,stu+num,cmp); //数组名 数组名+数组长度
知识总结:
1.strcmp(a,b)用来从左至右比较两个char型数组的字典序大小
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char a[3];
char b[3];
scanf("%s %s",a,b);
//当a的字典序列>b的字典序列时,返回正数
//当a的字典序列<b的字典序列时,返回复数
//当a的字典序列=b的字典序列时,返回0
}