随记一道在pta做作业的时候备受折磨的一道题
原题(代码部分往下划,有帮助的话点个赞呗qaq):
身份证号码共18位,其中第7到14位是出生日期。编写程序,输入n(n<20)个身份证号码,根据其中出生日期按年龄从大到小排序后输出。其中要求自定义函数int birthday(char s[ ]),其功能是从一个身份证信息s中生成整数的出生日期并返回。
输入格式:
先输入n,再输入n个身份证号码。
输出格式:
按年龄从大到小排序后输出身份证号码。
输入样例:
在这里给出一组输入。例如:
3
330103200110121513
330602199007191214
330104200106111313
输出样例:
在这里给出相应的输出。例如:
330602199007191214
330104200106111313
330103200110121513
我的解答:
这个题其实有高级的做法也有低级的做法,我的做法相对比较低级(因为我萌新),折腾了两个晚上我才琢磨出这个解法,可能有优化方案,欢迎留言交流qaq
首先,这个题有个坑,就是他最后让按照年龄由大到小排序,而不是日期由大到小排序。年龄由大到小反而是日期由小到大。这个涉及到后面冒泡法排序的时候if判断条件是什么。
下面关于函数birthday:
因为题目要求这个函数的参数是一个数组,然后返回值是一个整数,
我理解的是要从身份证号中提取一个八位数,后面冒泡法比较的就是这些八位数的大小
其实有一说一,题目中说要通过这样一个函数来实现大小比较反而是简化了这个情景的处理方法。
因为如果他没这么说的话,一般想到的直接思路就是分别提取出来年月日然后先比年,再比月,最后比日,这样虽然严谨,但其实会麻烦很多。
于是我用了如下的方法:
从身份证号的第七位开始,依次往后提取8个数分别乘以10的多少次方然后加在一起组成八位数
你比如说生日是19970528,就要把1乘以1e+7,加上9乘以1e+6,加上9乘以1e+5,以此类推:
long long birthday(char*p){
long long date = 1e+7*(*(p+6)-48)+1e+6*(*(p+7)-48)
+1e+5*(*(p+8)-48)+1e+4*(*(p+9)-48)+1e+3*(*(p+10)-48)
+1e+2*(*(p+11)-48)+10*(*(p+12)-48)+(*(p+13)-48);
return date;
}
至于这里为啥每一项都要减去48,这是因为指针指向的是字符,其值是每个数字的ASCII码,而数字的ASCII码比他本来的值大48。比如 '3' 的ASCII码是51,你想把 '3' 变成整数3就得 '3' -48。
整体上的思路如下:
首先定义n,并读入第一行的值,赋给n;
然后想办法依次把每个身份证号作为字符串传进birthday函数然后返回各自的日期数;
然后比较这些日期数,用冒泡法很容易实现;
最后按照排完序的从小到大的顺序输出就行了呗。
但是!到此并没有结束,事情远远没有想象的那么简单。你把每个身份证号作为字符串传进函数然后返回各自的日期数之后,用冒泡法比较这些日期,到这里都没问题。但是你忽略了一个问题。因为最后要求输出的是排序后的身份证号,所以你要输出的是身份证号而不是日期数。但是你在冒泡法比大小的时候做了什么?你把所有的日期原本的序号全搞乱了!
以输入样例为例,原来的序号是这样:
①20011012 对应330103200110121513
②19900719 对应330602199007191214
③20010611 对应330104200106111313
然后冒泡法结束之后却成了这样:
①19900719 对应330103200110121513
②20010611 对应330602199007191214
③20011012 对应330104200106111313
你日期数确实是调换对了,但是你并没有改变原来身份证号的顺序啊!
等你最后输出的时候,是遍历序号①到③来输出身份证号数组的,所以你输出的顺序和输入的顺序一样,铁错!
于是,解决方法:
那就是采用结构,把一个日期数和一个身份证数组放在一个结构里面,调换位置的时候不仅调换每个结构里的日期数,还要调换每个结构里的身份证数组。这样就能同步调换了,不会导致原来的日期数和身份证号数组脱节的情况。
另外还要注意,定义的结构要变成结构数组来使用。因为一个结构存一组数据(每组数据包含一个整数和一个字符串),但是我们要存n组数据。
我的代码:
#include <>
long long birthday(char*);
int main(){
int n;
scanf("%d",&n);
//救世的结构出现了!
struct statics{
long long date;//这个long long类型的date用来存转换后的日期数
char number[19];//这个字符数组用来存身份证号字符串,之所以19位是因为最后要放'0'
};
struct statics aSta[n];//这个结构要作为一个“结构数组”,因为要存3组数据
char id[19];
for(int j=0;j<n;j++){
scanf("%s",id);
char *p=id;
aSta[j].date=birthday(p);
for(int i=0;i<19;i++){
aSta[j].number[i] = id[i];
}
}
char res[n];
/*冒泡法比较结构数组中每个单位中的date值:
至于冒泡法是什么,可以自行搜索,因为这是随记我就不多说了,写完这个还有别的事要做qaq*/
for(int i=0;i<n-1;i++){
for(int j=0;j<n-i-1;j++){
if(aSta[j].date > aSta[j+1].date){
/*如果前一个结构中的date值大于后一个结构中的date值,
就把这两个结构的date值对调:*/
long long tempDATE = aSta[j].date;
aSta[j].date = aSta[j+1].date;
aSta[j+1].date = tempDATE;
/*同时,光对调这两个结构的date值是不够的,
我们还需要同步对调这两个结构中的number数组:*/
//方法是定义一个中间数组tempNUMB[19]:
char tempNUMB[19];
//首先,遍历 tempNUMBER数组 和 前一个结构中的number数组 来赋值:
for(int k=0;k<19;k++){
tempNUMB[k] = aSta[j].number[k];
}
//然后,遍历 前一个结构中的number数组 和 后一个结构中的number数组 来赋值:
for(int k=0;k<19;k++){
aSta[j].number[k] = aSta[j+1].number[k];
}
//最后,遍历 后一个结构中的number数组 和 tempNUMBER数组 来赋值:
for(int k=0;k<19;k++){
aSta[j+1].number[k] = tempNUMB[k];
}
}
else continue;
/*如果一开始就满足了前一个结构的date值小于后一个结构的date值,
就continue跳过这一轮对调,防止超时*/
}
}
//下面输出,用puts函数写比较方便,当然也可以用printf %s。
for(int i=0;i<n;i++){
puts(aSta[i].number);
}
return 0;
}
/*下面定义函数birthday:
因为题目要求这个函数的参数是一个数组,然后返回值是一个整数,
我理解的是要从身份证号中提取一个八位数。
于是我用了如下的方法:
从身份证号的第七位开始,依次往后提取8个数分别乘以10的多少次方
你比如说生日是19970528,就要把1乘以1e+7,加上9乘以1e+6,加上9乘以1e+6,以此类推:*/
long long birthday(char*p){
long long date = 1e+7*(*(p+6)-48)+1e+6*(*(p+7)-48)
+1e+5*(*(p+8)-48)+1e+4*(*(p+9)-48)+1e+3*(*(p+10)-48)
+1e+2*(*(p+11)-48)+10*(*(p+12)-48)+(*(p+13)-48);
/*至于这里为啥每一项都要减去48,这是因为指针指向的是字符,其值是每个数字的ASCII码,
而数字的ASCII码比他本来的值大48。比如'3'的ASCII码是51,你想把他变成整数3就得'3'-48。
*/
return date;
}
补充:
在这个结构版本之前我还写了一个二维数组的版本,就是把身份证号转换成一个十八位整数,和那个八位日期数一起存在二维数组里,这个方法在VSCode里能输出正确结果,但是在pta里输出全是0,不知道pta咋了,可能是嘲讽我用的方法太低级太麻烦了罢(悲)。
这个题肯定有更优解,我只是一个甚至尚未熟练掌握指针的萌新。有啥优化方法欢迎留言。