【C语言16天强化训练】从基础入门到进阶:Day 3


🔥个人主页艾莉丝努力练剑

❄专栏传送门:《C语言》《数据结构与算法》C语言刷题12天IO强训LeetCode代码强化刷题洛谷刷题C/C++基础知识知识强化补充C/C++干货分享&学习过程记录

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平

前言:距离我们学完C语言已经过去一段时间了,在学习了初阶的数据结构之后,博主还要更新的内容就是【C语言16天强化训练】,之前博主更新过一个【C语言刷题12天IO强训】的专栏,那个只是从入门到进阶的IO模式真题的训练。【C语言16天强化训练】既有IO型,也有接口型。今天依然是训练五道选择题和两道编程算法题,希望大家能够有所收获!



目录

正文

一、五道选择题

[1.1 题目1](#1.1 题目1)

[1.2 题目2](#1.2 题目2)

[1.3 题目3](#1.3 题目3)

[1.4 题目4](#1.4 题目4)

[1.5 题目5](#1.5 题目5)

二、两道算法题

[2.1 记负均正](#2.1 记负均正)

题目理解:

[2.2 旋转数组的最小数字](#2.2 旋转数组的最小数字)

题目理解:

结尾


正文

一、五道选择题

1.1 题目1

**题干:**已知函数的原型是:int fun(char b[10], int *a); ,假设定义:char c[10];int d; ,那么正确的调用语句是( )

A. fun(c,&d); B. fun(c,d); C. fun(&c,&d); D. fun(&c,d);

解析: 正确的调用语句是A. fun(c,&d);。我们先分析一下函数原型int fun(char b[10], int *a);表示函数fun的第一个参数是一个字符数组(或者说字符指针),第二个参数是一个整型指针。

继续分析函数原型:

(1)第一个参数char b[10]:可以直接传递字符数组c数组名(c会退化为指针);

(2)第二个参数int *a):需要传递一个整型变量的地址,因此d应该通过&d传递其地址。

这里我们定义的是char c[10];int d;,所以关键就在于------

1.数组作为参数 :**char c[10]**作为参数时,实际传递的是char*(d退化为指针);

2.指针参数int*a 必须传入地址(如&d),不能直接传值(如d)。

补充:

这里唯一正确的调用方式是A. fun(c, &d);****

B选项是错误的,因为它没有传递指针给 int *a

1.2 题目2

**题干:**请问下列表达式哪些会被编译器禁止()【多选】

cpp 复制代码
int a = 248, b = 4;
int const *c = 21;
const int *d = &a;
int *const e = &b;
int const * const f = &a;

A. *c = 32; B. *d = 43 C. e=&a D. f=0x321f

解析: 题目问的是"哪些表达式会被编译器禁止",即哪些操作是非法(违反const规则)的。

我们注意以下几点------

(1)const 在 * 左侧(如const int* 或 int const*):指向的值不可变,指针可变

(2)const 在 * 右侧(如 int *const):指针不可变,指向的值可变

(3)const 在 * 两侧(如int const *const):指针和指向的值均不可变

所有选项(A、B、C、D)均违反const规则,全部会被编译器禁止 。若题目要求"多选",应全选

我们再深挖一下选项看看------

问:为什么D选项有两个const,分别都禁止了什么?

1、第一个const(左侧的int const或const int)

禁止的操作不能通过指针 f 修改它指向的值(即 *f 是只读的)。

举个例子:

cpp 复制代码
*f = 100;  // 非法!编译器会报错,因为 *f 是常量。

**作用(限制)范围:**限制的是指针解引用后的值(*f),与指针本身的存储无关。

2、第二个 const(右侧的*const)

禁止的操作不能修改指针 f 本身的值 (即 f 不能指向其他地址)。

举个例子:

cpp 复制代码
f = &b;    // 非法!编译器会报错,因为 f 是常量指针。
f = NULL;  // 非法!

**作用(限制)范围:**限制的是指针变量 f 存储的地址(指针自身的值)。

总结

声明形式 禁止的操作 允许的操作
int const *const f 1. 修改 *f(值) 2. 修改 f(指针) 1. 读取 *f 2. 读取 f(地址)

**补充:**两个const共同确保了指针和指向的数据都不可变。

1.3 题目3

**题干:**以下程序的输出结果为( )

cpp 复制代码
#include <stdio.h>
int i;
void prt()
{
    for (i = 5; i < 8; i++)
    printf("%c", '*');
    printf("\t");
} 
int main()
{
    for (i = 5; i <= 8; i++)
    prt();
    return 0;
}

A. *** B. *** *** *** *** C. *** *** D. * * *

解析:注意这里i是一个全局变量, i是全局变量,在ptr() 和main() 中共享。

这里 ptr() 中的 for (i = 5; i < 8; i++)会执行 3 次循环(i = 5,6,7),每次打印一个' * ',因此输出 *** 。之后打印一个制表符**\t(tab)。**

main()函数中,for (i = 5; i <= 8; i++)会调用 ptr() 4 次(i = 5,6,7,8)。

每次调用ptr()的时候:i 被 ptr() 的 for 循环修改为 8(循环结束后i++使 i 变为8),然后回到main() 的for循环,i++使 i 变为 9,从而终止循环。

因此,只有第一次调用 ptr() 会正常输出*** ,后续调用因为 i 的全局性导致循环条件直接不满足,因而无输出。

实际输出***(仅第一次调用有效,其余调用因 i 的值被修改而无输出),答案是A选项

**注意:**这里的 * 紧密相连,中间是没有空格的。

1.4 题目4

**题干:**下面代码段的输出是( )

cpp 复制代码
int main()
{
    int a=3;
    printf("%d\n",(a+=a-=a*a));
    return 0;
}

A. -6 B. 12 C. 0 D. -12

**解析:**题目是(a+=a-=a*a),计算过程如下:

a * a9

a -= 93 - 9 = -6

此时 a = -6,我们把-6代入到后面的赋值操作------

a += -6-6 + (-6) = -12

这样一来,答案就是选项D. -12

1.5 题目5

**题干:**下列不能实现死循环的是( )

A. while(1){} B. for(;1;){} C. do{}while(1); D. for(;0;){}

解析:如下所示------

A. while(1){} ------

1 是恒为真条件,因此这是一个典型的死循环,故能实现死循环。

B. for(;1;){} ------

for 循环的中间条件为 1(恒为真),相当于 while(1){} ,故能实现死循环。

C. do{}while(1); ------

do{}while(1); 的条件为 1(恒真),会无限循环,故能实现死循环。

D. for(;0;){} ------

for 循环的中间条件为 0(恒为假),循环体根本不会执行,不能实现死循环。

题目问的是"不能实现死循环"的选项,因此正确答案是D选项

选择题答案如下:

1.1 A

1.2 ABCD

1.3 A

1.4 D

1.5 D

校对一下,大家都做对了吗?

二、两道算法题

2.1 记负均正

题目链接:HJ97 记负均正

题目描述:

题目理解:

我们需要统计负整数的个数并计算正整数的平均值。

这道题也是IO型的,下面是C语言的模版(IO型就不用管它们了)------

代码演示:

cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS  1
//C语言
#include <stdio.h>

int main() 
{
    int n;
    scanf("%d", &n);  // 读取整数个数

    int negative_count = 0;  // 负整数计数器
    int positive_count = 0;  // 正整数计数器
    int positive_sum = 0;    // 正整数总和

    for (int i = 0; i < n; i++) 
    {
        int num;
        scanf("%d", &num);  // 读取每个整数

        if (num < 0) 
        {
            negative_count++;  // 统计负数
        }
        else if (num > 0) 
        {
            positive_count++;  // 统计正数
            positive_sum += num;  // 累加正数
        }
        // 0不做处理
    }
    // 输出负整数个数
    printf("%d ", negative_count);

    // 计算并输出正整数的平均值
    if (positive_count > 0) 
    {
        double average = (double)positive_sum / positive_count;
        printf("%.10lf\n", average);  // 输出10位小数保证精度
    }
    else 
    {
        printf("0.0\n");  // 没有正整数时输出0.0
    }
    return 0;
}

这道题也是C语言中一道比较经典的算法题目。

时间复杂度O(n)

空间复杂度O(1)

当然博主还有一个更加简单的解法,这个解法大家理解起来就简单了------

代码演示:

cpp 复制代码
//C语言自己写的版本------全局变量array
#include <stdio.h>

int main()
{
    int n = 0;
    scanf("%d\n", &n);
    int array;
    int num1 = 0;
    int num2 = 0;
    double sum = 0.0;
    for (int i = 0; i < n; i++)
    {
        scanf("%d ", &array);
        if (array < 0)
        {
            num1 = num1 + 1;
        }
        else if (array > 0)
        {
            num2 = num2 + 1;
            sum += array;
        }
    }
    double average = num2 > 0 ? sum / num2 : 0.0;

    printf("%d %.11lf", num1, average);
    return 0;
}

也可以这样,都是一样的道理------

代码演示:

cpp 复制代码
//C语言------局部变量arr数组
#include <stdio.h>

int main()
{
    int n = 0;
    scanf("%d\n", &n);
    int num1 = 0;
    int num2 = 0;
    double sum = 0.0;
    for (int i = 0; i < n; i++)
    {
        int arr;
        scanf("%d ", &arr);
        if (arr > 0)
        {
            num1 = num1 + 1;
            sum += arr;
        }
        else if (arr < 0)
        {
            num2 = num2 + 1;
        }
    }
    double average = num1 > 0 ? sum / num1 : 0.0;
    printf("%d %.11lf", num2, average);
    return 0;
}

我们学习了C++之后也可以尝试用C++来实现一下,看看自己前段时间C++学得怎么样------

cpp 复制代码
//C++实现
#include <iostream>
#include <iomanip> // 用于控制输出精度
using namespace std;

int main() 
{
    int n;
    cin >> n;  // 读取整数个数

    int negative_count = 0;  // 负整数计数器
    int positive_count = 0;  // 正整数计数器
    int positive_sum = 0;    // 正整数总和

    for (int i = 0; i < n; i++) 
    {
        int num;
        cin >> num;  // 读取每个整数

        if (num < 0) 
        {
            negative_count++;  // 统计负数
        }
        else if (num > 0) 
        {
            positive_count++;  // 统计正数
            positive_sum += num;  // 累加正数
        }
        // 0不做处理
    }

    // 输出负整数个数
    cout << negative_count << " ";

    // 计算并输出正整数的平均值
    if (positive_count > 0) 
    {
        double average = static_cast<double>(positive_sum) / positive_count;
        // 设置输出精度为10位小数
        cout << fixed << setprecision(10) << average << endl;
    }
    else 
    {
        cout << "0.0" << endl;  // 没有正整数时输出0.0
    }

    return 0;
}

时间复杂度:O(n),空间复杂度:O(1)

这个目前要写出来非常考验C++的学习情况,大家可以尝试去写一写,优先掌握C语言的写法,博主还没有介绍C++的算法题,之后会涉及的,敬请期待!

2.2 旋转数组的最小数字

题目链接:JZ11 旋转数组的最小数字

题目描述:

题目理解:

我们需要在旋转后的非降序数组中找到最小数字。由于题目要求时间复杂度为O(logn),我们使用二分查找算法来实现。

这道题是接口型的,下面是C语言的模版(IO型就不用管它们了)------

代码演示:

cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS  1
//C语言
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 *
 * @param nums int整型一维数组
 * @param numsLen int nums数组长度
 * @return int整型
 */
int minNumberInRotateArray(int* nums, int numsLen)
{
    if (numsLen == 0) return 0;  // 处理空数组情况

    int left = 0;
    int right = numsLen - 1;

    while (left < right)
    {
        int mid = left + (right - left) / 2;  // 防止溢出

        if (nums[mid] > nums[right])
        {
            // 最小值在右半部分
            left = mid + 1;
        }
        else if (nums[mid] < nums[right])
        {
            // 最小值在左半部分或就是mid
            right = mid;
        }
        else
        {
            // 当nums[mid] == nums[right]时,无法判断,缩小右边界
            right--;
        }
    }

    return nums[left];
}

这道题是C语言中一道比较经典的题目。

当然博主还有一个更加简单的解法,这个解法大家理解起来就简单了。

我们如果学习了C++也可以尝试用C++实现一下------

cpp 复制代码
//C++实现
class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型vector
     * @return int整型
     */
    int minNumberInRotateArray(vector<int>& nums) {
        // write code here
        if (nums.empty()) return 0;  // 处理空数组情况

        int left = 0;
        int right = nums.size() - 1;

        while (left < right) {
            int mid = left + (right - left) / 2;  // 防止溢出

            if (nums[mid] > nums[right]) {
                // 最小值在右半部分
                left = mid + 1;
            } else if (nums[mid] < nums[right]) {
                // 最小值在左半部分或就是mid
                right = mid;
            } else {
                // 当nums[mid] == nums[right]时,无法判断,缩小右边界
                right--;
            }
        }

        return nums[left];
    }
};

时间复杂度O(logn)

空间复杂度O(1)

目前要写出来C++的写法是比较考验前面C++的学习情况的,当然大家可以尝试去写一写,优先掌握C语言的写法,博主还没有介绍C++的算法题,之后会涉及的,敬请期待!


结尾

往期回顾:

【C语言16天强化训练】从基础入门到进阶:Day 2

【C语言16天强化训练】从基础入门到进阶:Day 1

结语:感谢大家的阅读,记得给博主"一键四连",感谢友友们的支持和鼓励!