前言
本期是C语言刷题的第四期,该系列的内容都为本人在刷题中遇到的,不同类型且具有典范性的题目,这些题目的算法思想非常直到学习,以及对基础的考察都十分细致,如对知识有遗忘,可参考C语言系列专栏。
1. 选择题
📖题目1:
设变量已正确定义,以下不能统计出一行中输入字符个数(不包含回车符)的程序段是( )
A:
n=0; while(ch=getchar()!='\n')n++;
B:
n=0; while(getchar()!='\n')n++;
C:
for(n=0;getchar()!='\n';n++);
D:
n=0; for(ch=getchar();ch!='\n';n++);
✨题目解析:
这道题目考察的是循环
这道题目乍一看好像都可以计算字符个数,如果你也这么想,这就说明你对循环部分的知识理解不够扎实。
对于for循环,其中第一项初始化表达式只执行一次,因此ch只从输入流中取一个字符,之后就再不会取字符,因此会死循环
正确答案:D
📖题目2:
若运行以下程序时,从键盘输入 ADescriptor<回车> ,则下面程序的运行结果是( )
#include <stdio.h> int main() { char c; int v0=0,v1=0,v2=0; do { switch(c=getchar()) { case'a':case'A': case'e':case'E': case'i':case'I': case'o':case'O': case'u':case'U':v1 += 1; default:v0+= 1;v2+=1; } }while(c!='\n'); printf("v0=%d,v1=%d,v2=%d\n",v0,v1,v2); return 0; }
A: v0=7, v1=4, v2=7
B: v0=8, v1=4, v2=8
C: v0=11, v1=4, v2=11
D: v0=12, v1=4, v2=12
✨题目解析:
这道题目主要考察switch语句。
代码switch语句中没有break,则每次找到入口进入后,顺序执行到代码块结束为止。例如当c为'A'时,从case 'A'进入,先后执行v1+=1;v0+=1;v2+=1;,而当c为'p'时,从default进入,先后执行v0+=1;v2+=1;,容易看出最终v0和v2是相等的
正确答案:D
📖题目3:
执行下面的程序段,语句3的执行次数为( )
for(i = 0; i <= n-1; i++) // 语句(1) for(j = n; j > i; j--) // 语句(2) state; // 语句(3)
A: n(n+2)/2
B: (n-1)(n+2)/2
C: n(n+1)/2
D: (n-1)(n+2)
✨题目解析:
外循环有n次,当i=0,内循环为n次,当i=1,内循环为n-1次,当i=2时,内循环为n-2次,以此类推,总次数为n+(n-1)+(n-2)+......+2+1,就是个等差数列,等于n(n+1)/2
正确答案:C
📖题目4:
对于代码段,下面描述正确的是( )
t=0; while(printf("*")) { t++; if (t<3) break; }
A、其中循环控制表达式与0等价
B、其中循环控制表达式与'0'等价
C、其中循环控制表达式是不合法的
D、以上说法都不对
✨题目解析:
printf(“*”)函数调用的返回值是字符串中字符的个数,即为1。所以while后面的条件恒为真,所以循环控制表达式与'0'是等价的(字符'0'不是0)。正确答案是B
正确答案:B
📖题目5:
在c语言中,一个函数不写返回值类型,默认的返回类型是( )
A、int
B、char
C、void
D、都不是
✨题目解析:
一个函数不写返回值类型,默认的返回类型是int,但不提倡这么做
正确答案:A
2. 编程题
📖题目一
题目描述:
示例:
题目链接:
✨题目解析:
这道题目的解法有很多,但今天我主要向大家介绍一种新的解题思想与方法,这种方法可适用于许多类似的题目当中,那就是——标记法
思路:
使用标记的方式就可以找出重复的数字,数组中出现过哪个数字就把对应数字作为下标在对应位置,置为1。表示已经标记出现过,如果哪个数据对应位已经置1,则表示就是重复的数字。
有了这个思路我们带入到题目当中,通过这个方法我们可以找到重复的数据,有了重复的数字,拿 [1, n] 的总和减去,去掉重复数据的数组总和,就是丢失的数据。
具体代码如下:
int* findErrorNums(int* nums, int numsSize, int* returnSize){ *returnSize=2; int* arr=(int*)malloc(sizeof(int)*(numsSize+1)); int* ret=(int*)malloc(sizeof(int)*2); int sum1=0,sum2=0; for(int i=0;i<numsSize;i++) { if(arr[nums[i]]==1) //这个数字在上边数组的对应位置已经置过1了,则重复 { ret[0]=nums[i]; //找到重复的数字 } arr[nums[i]]=1; //将标记数组的对应数据位置1 sum1+=i+1; // 1~n的求和 sum2+=nums[i]; //当前数组中的数据求和(多了一个重复的,少了一个丢失的) } ret[1]=sum1-sum2+ret[0]; //原始总和,减去去掉重复后的当前总和就是丢失的数字 free(arr); //最后释放掉用于标记的数组 return ret; }
注意:新开的数组要比原数组的空间多一个,例如原数组大小为4(也就是n),数据是1、2、2、4,数据是从1到n的整数,如果用标记法,就需要使用原数组中的数据作为数组下标进行标记,而数组下标从0开始,要想不越界就必须创建5个空间的数组,这样才能有下标4.
📖题目二
题目描述:
示例:
题目链接:
✨题目解析:
我们已经知道了标记法,接下来我们趁热打铁,继续来看这道题,这道题目的条件非常适合使用标记法。
思路:
首先把a字符( ascii 值为 97 )作为下标,将标记数组的第 97 位置 1 ,下次如果还有 a 字符出现,到下标 'a' 或者 97 的位置一看是1就表示a已经统计过了。
代码实现如下:
#include <stdio.h> int main() { char str[501]; char* pst=str; int arr[128]={0}; int count=0; scanf("%s", str) ; while(*pst!='\0') { if(arr[*pst]!=1) { count++; } arr[*pst++]=1; } printf("%d",count) ; return 0; }
通过这两道题目的练习,我们会发现,标记法这种方法具有局限性,使用数组来标记,就必须要开足够大的空间,如果给的数组范围太大,如1000000,这种开辟数组就非常难了,空间占用也大,所以标记法不适用于数据范围过大的情况。
总结
本期我们提供了对C语言基础练习的选择题,以及新算法思想的实践练习,本系列内容也属于我个人编程语言学习笔记的一个总结,最后,我要衷心感谢每一位读者,你们的支持和参与是我前进的动力。希望你们在C语言的学习和编程之路上继续努力,不断追求卓越。祝愿你们在未来的编程之旅中取得更大的成功和成就!