习题练习 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;
    }
}

总结

重要的事说三遍!
加油!加油!加油!