习题练习 C语言(暑期第二弹)
编程能力小提升!
前言
重要的事说三遍!
学习!学习!学习!
一、表达式判断
对于代码段,下面描述正确的是( )
t=0;
while(printf("*"))
{
t++;
if (t<3)
break;
}
A: 其中循环控制表达式与0等价
B: 其中循环控制表达式与’0’等价
C:其中循环控制表达式是不合法的
D: 以上说法都不对
题目解析:
因print(“*”)函数调用的返回值是字符串中字符的个数,即为1。
所以while后面的条件恒为真,所以循环控制表达式与’0’是等价的(字符’0’不是0)。
正确答案是B
题目答案:
B
二、Assii码的理解应用
以下程序运行时,若输入 1abcedf2df<回车> 输出结果是( )
#include <stdio.h>
int main()
{
char ch;
while ((ch = getchar()) != 'n')
{
if (ch % 2 != 0 && (ch >= 'a' && ch <= 'z'))
ch = ch - 'a' + 'A';
putchar(ch);
}
printf("n");
return 0;
}
A: 1abcedf2df
B: 1ABCEDF2DF
C: 1AbCEdf2df
D: 1aBceDF2DF
题目解析:
程序首先考虑ch的ASCII码值是不是奇数,再看是不是小写字母,同时满足时被改为大写字母
题目答案:
C
三、循环跳出判断
我们知道C语言的 break 语句只能跳出离它最近的一层循环,可是有时候我们需要跳出多层循环,下列跳出多层 循环的做法正确的是【多选】( )
A: 将程序写成函数用return结束函数,便可跳出循环
B: 修改外层循环条件例如
for( int i = 0 ; i < MAX1 ; i ++ )
{
for( int j = 0 ; j < MAX2 ; j ++ )
{
if( condition )
{
i = MAX1;
break;
}
}
}
C:在外层循环设置判断条件例如
for( ; symbol != 1 && condition2 ; )
{
for( ; symbol != 1 && condition3 ; )
{
if( condition1 )
symbol = 1 ;
}
}
D: 在外层循环后面加入break例如
for( ; condition2 ; )
{
for( ; condition3 ; )
{
if( condition1 )
symbol = 1 ;
}
if(symbol == 1 )
break ;
}
题目解析:
此题旨在整理跳出多层循环的方法,每个选项都是正确的,代码为伪代码,condition代表逻辑表达式
题目答案:
ABCD
四、数字在升序数组中出现的次数
题目链接OJ链接
题目解析:
采用遍历也能搞定,不过数组为非降序,采用二分查找的思想最优,先二分找到最左边的数字位置,再二分查找最右边的数字位置,两个位置相减+1就是长度了
中间比找的值大:则要找的数字肯定在右边, left = mid + 1;
中间比找的值小:则要找的数字肯定在左边, right = mid - 1;
中间值与找的值相同:
找的最左边数字:如果mid就是left,则返回mid就行,否则重置right=mid-1,把中心不断向左偏移
找的最右边数字:如果mid就是right,则返回mid就行,否则重置left=mid+1,把中心不断向右偏移
题目答案:
int get_last_or_first_idx(int* data, int len, int k,int flag) {
int left = 0, right = len - 1, mid;//左右指针下标及中间结点下标
while (left <= right) {
mid = left + (right - left) / 2;//找到中间结点
if (data[mid] > k)
right = mid - 1;//如果中间节点大于k,则将右节点左移
else if (data[mid] < k)
left = mid + 1;//如果中间节点小于k,则将左节点右移
else {//如果中间节点值等于k,则判断flag的值来区分找的是哪边
if (flag == 0) {
if (mid == left || data[mid - 1] != k) return mid;//如果中间结点等于k,且前一个结点不等于k,以及和left位置相同,则找到最左边的k,返回下标
else right = mid - 1;
} else {
if (mid == right || data[mid + 1] != k) return mid;//如果中间结点等于k,且后一个结点不等于k,以及和right位置相同,则找到最右边的k,返回下标
else left = mid + 1;
}
}
}
return -1;
}
int GetNumberOfK(int* data, int dataLen, int k ) {
if (dataLen == 0) return 0;
int left = get_last_or_first_idx(data, dataLen, k, 0);
int right = get_last_or_first_idx(data, dataLen, k, 1);
if (left == -1 && right == -1) return 0;
return right - left + 1;//左右下标相减得到k的个数
}
五、整数转换
题目链接:OJ链接
提示:
A,B范围在[-2147483648, 2147483647]之间
题目解析:
其实问需要修改多少个比特位,问的就是有多少个比特位不同而已,因为有多少位不同就修改多少位而已;
题目答案:
int convertInteger(int A, int B){
int a=A^B;//相同为0,相异为1
double count=0;
for(int i=0;i<32;i++){
if((a>>i)&1==1){//通过与1与,得到求出的数二进制序列中有多少个1,从而确定个数;
count++;
}
}
return count;
}
六、循环语句的应用
以下叙述中正确的是( )
A: 只能在循环体内和switch语句体内使用break语句
B:当break出现在循环体中的switch语句体内时,其作用是跳出该switch语句体,并中止循环体执行
C:continue语句的作用是:在执行完本次循环体中剩余语句后,中止循环
D:在while语句和do-while语句中无法使用continue语句
题目解析:
break语句通常用在循环语句和switch语句中。当break用于switch语句中时,可使程序跳出switch而执行switch以后的语句;
当break语句用于do-while、for、while循环语句中时,可使程序终止循环而执行循环后面的语句,即满足条件时便跳出循环。
continue语句的作用是跳过循环体中剩余的语句而强行执行下一次循环。B、C和D三个选项中均有错误。因此A选项正确
题目答案:
A
七、函数调用
设函数 fun 和实参数组的说明是如下形式,则对函数的调用语句中,正确的是( )
void fun(char ch,float x[]);
float a[10];
A: fun(“asd” , a[]); B: fun(‘x’ , A); C: fun(‘68’ , 2.8); D: fun(32 , a);
题目解析:
A选项数组传参只需要写数组名就行,a[]时错误的,B选项第二个参数写成了大写,错了。C选项第二个参数是浮点数,但是fun函数的第二参数是数组不匹配,fun函数参数x需要传一个数组或者float *指针,只有D选项的形式是正确的。
题目答案:
D
八、两个数组的交集
题目链接:OJ链接
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <=1000
题目解析:
A选项数组传参只需要写数组名就行,a[]时错误的,B选项第二个参数写成了大写,错了。C选项第二个参数是浮点数,但是fun函数的第二参数是数组不匹配,fun函数参数x需要传一个数组或者float *指针,只有D选项的形式是正确的。
题目答案:
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
static int arr[1000];//定义静态,防止销毁
*returnSize = 0;//返回数组大小
for (int i = 0; i < nums1Size; i++) {//将nums1中的每个数遍历nums2找到相同的数
for (int j = 0; j < nums2Size; j++) {
if (nums1[i] == nums2[j]) {
int k = 0;
for (k = 0; k < *returnSize; k++) {//遍历arr数组看是否已经存在,找到了返回下标
if (arr[k] == nums2[j])
break;
}
if (k == *returnSize) {//如果k等于*returnSize则说明数组中不存在该数,则向数组中加入该数
arr[*returnSize] = nums2[j];
(*returnSize)++;//返回数组长度+1
}
}
}
}
return arr;
}
九、C语言基础
以下对C语言函数的有关描述中,正确的有【多选】( )
A: 在C语言中,一个函数一般由两个部分组成,它们是函数首部和函数体
B:函数的实参和形参可以是相同的名字
C: 在main()中定义的变量都可以在其它被调函数中直接使用
D:在C程序中,函数调用不能出现在表达式语句中
题目解析:
主函数中定义的局部变量只在主函数中有效,因为主函数也是一个函数,它与其他函数是平行关系,C错误;当函数有返回值时,可以出现在表达式中,D错误
题目答案:
AB
十、图片整理
题目链接:OJ链接
题目解析:
这道题考察的其实就是字符排序,每个 ascii 字符在内存都有一个对应的 ascii 值,通过内存中数据的存储进行排序就行。
冒泡排序:相邻数据之间进行比较交换,将较大或较小的数据向后推到数组末尾,然后开始下一轮次大数据的冒泡
过程。
题目答案:
方法一:
#include <stdio.h>
void Qsort(char*arr,int len){
for(int i=0;i<len;i++){//冒泡排序
for(int j=0;j<len-i-1;j++){
if(arr[j]>arr[j+1]){
char temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
int main() {
char arr[1001] = {0};
while (scanf("%s", arr)!= EOF) {
char digit[1001] = {0};//将数字入到该数组
int digits = 0;//数字个数
char upper[1001] = {0};//将大写字母入到该数组
int uppers = 0;//大写字母个数
char lower[1001] = {0};//将小写字母入到该数组
int lowers = 0;//小写字母个数
char* ptr = arr;
while (*ptr) {//查找并添加
if (*ptr >= '0' && *ptr <= '9') {
digit[digits] = *ptr;
digits++;
}
if (*ptr >= 'A' && *ptr <= 'Z') {
upper[uppers] = *ptr;
uppers++;
}
if (*ptr >= 'a' && *ptr <= 'z') {
lower[lowers] = *ptr;
lowers++;
}
ptr++;
}
//进行排序
Qsort(digit,strlen(digit));
Qsort(upper,strlen(upper));
Qsort(lower,strlen(lower));
//进行输出
for (int j = 0; j < digits; j++) {
printf("%c", digit[j]);
}
for (int j = 0; j < uppers; j++) {
printf("%c", upper[j]);
}
for (int j = 0; j < lowers; j++) {
printf("%c", lower[j]);
}
}
return 0;
}
方法二:
#include <stdio.h>
int main()
{
char str[1024] = {0};
while(gets(str)) {
int len = strlen(str);
for (int i = 0; i < len; i++) {
for (int j = 1; j < len - i; j++) {
if (str[j] < str[j - 1]) {
char ch = str[j - 1];
str[j - 1] = str[j];
str[j] = ch;
}
}
}
printf("%sn", str);
}
return 0;
}
十一、数组的引用
若有定义: int a[2][3]; ,以下选项中对 a 数组元素正确引用的是( )
A: a[2][0]
B: a[2][3]
C:a[0][3]
D: a[1>2][1]
题目解析:
本题主要考虑数组越界访问的情况,二维数组的行和列都是从0开始的,对于a数组来说,行下标最大是1,列下标最大是2,D选项中1>2表达式的值是0,是正确的,其他选项行和列都可能存在越界,A是行越界,B是行和列都越界,C是列越界。
题目答案:
D
十二、数组的引用
已知 i,j 都是整型变量,下列表达式中,与下标引用 X[i][j] 不等效的是【多选】( )
A: (X[i]+j)
B:(X+i)[j]
C: *(X+i+j)
D: ((X+i)+j)
题目解析:
本题考查的是二维数组的元素访问,A选项是 正确的,X[i]就是第i行的数组名,数组名表示首元素的地址,X[i]表示第i行的第一个元素的地址,+j后就是第i行下标为j的元素的地址,整体解引用就是X[i][j],A正确。B选项因为[]的优先级高于*,所以代码相当于**((x+i)+j),X+i+j后就越界了,并不代表X[i][j],所以错误。C选项也明显不对,X是二维数组的数组名,数组名相当于第一行的地址X+i+j,跳过了i+j行,就越界了,C错误。D选项是标准的指针形式访问二位数组的一个元素。
题目答案:
BC
十三、字符个数统计
题目链接:OJ链接
题目解析:
本题思路,先建立一个大小为128的数组,从而保证0~127的各种字符都能在数组中找到它的assii值所对应的位置,将找到的位置变为1,最后将数组中的值相加,所得的就是字符的种类
题目答案:
#include <stdio.h>
int main() {
char arr[501]={0};
while (scanf("%s",arr) != EOF) {
char*ptr=arr;
int count[128]={0};
while(*ptr){
int temp=*ptr;//通过Assii码值作为下标找到count数组中对应的位置
count[temp]=1;//将值变为1;
ptr++;
}
int ret=0;
for(int i=0;i<=129;i++){//遍历相加
ret+=count[i];
}
printf("%dn",ret);
}
return 0;
}
十四、多数元素
题目链接:OJ链接
提示:
n == nums.length
1 <= n <= 5 * 104
-109 <= nums[i] <= 109
题目解析:
一个数组中有一个数字出现次数大于 n/2 ,从第 0 个字符开始,假设它就是最多的那个数字,遇到相同的数字则计数 +1 , 遇到不同的则计数 -1 ,其实就是互相消耗,等到计数为 0 的时候,表示本次互拼完毕,从下一个字符重新开始互拼,但是归根结底出现次数大于 n/2 的这个数字数量更多,因此也是最后保留的字符。
示例: “23335” 首先从字符 2 开始计数 1 ,遇到 3 ,不同则 -1 ,互拼消耗 重新从剩下的 “335” 开始的过程,这时候保存的字符为 3 ,遇到 3 则计数 +1 , 遇到5则计数 -1 ,在计数不为 0 时,走到末尾保存的字符就是个数超过n/2 的字符
题目答案:
int majorityElement(int* nums, int numsSize){
int count=1;//记录多数元素的个数与非多数元素出现个数的差
int more=nums[0];//将第一个作为多数元素
int i=1;
for(i;i<numsSize;i++){
if(more==nums[i]){//下标对应的数为多数元素,则count++
count++;
}
else{//下标对应的数不是多数元素,则count--
count--;
}
if(count==0){//如果count等于0,那就可能该数不是多数元素,将下一个作为多数元素继续遍历
more=nums[i+1];
}
}
if(count>0){//多数元素的个数与非多数元素出现个数的差大于0,则说明此时记录的元素多于总数的一半,为多数元素,
return more;
}
else{
return 0;
}
}
总结
重要的事说三遍!
加油!加油!加油!