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


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

❄专栏传送门:《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.1.1 题目理解](#2.1.1 题目理解)

[2.1.2 思路](#2.1.2 思路)

[2.2 除自身以外数组的乘积](#2.2 除自身以外数组的乘积)

[2.2.1 题目理解](#2.2.1 题目理解)

[2.2.2 思路](#2.2.2 思路)


一、五道选择题

1.1 题目1

**题干:**下列程序的输出是( )

cpp 复制代码
#include<stdio.h>
int main()
{
    int a [12]= {1,2,3,4,5,6,7,8,9,10,11,12},*p[4],i;
    for(i=0;i<4;i++)
        p[i]=&a [i*3];
    printf("%d\n",p[3][2]);
    return 0;
}

A. 上述程序有错误 B. 6 C. 8 D. 12

解析:正确答案是D选项。

(1)p 是一个包含4个指针的数组,每个指针指向 int;

(2)循环中,p[ 0 ] = &a[0], p[1] = &a[3], p[2] = &a[6], p[3] = &a[9];

(3)p[ 3 ][ 2 ]相当于 *(p[3] + 2),即从 p[3](指向 a[9])向后偏移2个元素,也就是 a[11],其值为12。

因此输出12,答案是D选项。

1.2 题目2

**题干:**二维数组X按行顺序存储,其中每个元素占1个存储单元。若 X [ 4 ][ 4 ] 的存储地址为Oxf8b82140 ,X [ 9 ][ 9 ] 的存储地址为 Oxf8b8221c ,则 X [ 7 ][ 7 ] 的存储地址为( )

A. Oxf8b821c4 B. Oxf8b821a6 C. Oxf8b82198 D. Oxf8b821c0

解析:正确答案是A选项。

假设数组有 rows 行、cols 列(未知),按行存储。
设起始地址为 base,则 X[i][j] 的地址 = base + i * cols + j

已知:

(1)X[4][4]:base + 4*cols + 4 = 0xf8b82140;

(2)X[9][9]:base + 9cols + 9 = 0xf8b8221c。
两式相减:
(9
cols + 9) - (4cols + 4) = 5cols + 5 = 0xf8b8221c - 0xf8b82140 = 0xDC(十进制220),所以 5*(cols + 1) = 220 ⇒ cols + 1 = 44 ⇒ cols = 43。
我们现在来求 X[ 7 ][ 7 ]------
地址 = base + 7cols + 7
已知 base + 4
cols + 4 = 0xf8b82140,所以
base + 7cols + 7 = (base + 4cols + 4) + (3cols + 3) = 0xf8b82140 + 343 + 3 = 0xf8b82140 + 129 + 3 = 0xf8b82140 + 132。

132的十六进制是 0x84,所以 0xf8b82140 + 0x84 = 0xf8b821c4

1.3 题目3

**题干:**以下哪个选项可以正确描述 sizeof(double) ( )

A. 一个整型表达式 B. 一个双精度型表达式 C. 一个不合法的表达式 D. 一种函数调用

解析:正确答案是A选项。

sizeof 是运算符,返回类型或对象的大小(字节数),类型是 size_t(无符号整型)。
所以 sizeof(double) 是一个整型表达式。

1.4 题目4

**题干:**下列代码运行后的结果是什么( )

cpp 复制代码
int main()
{
    char a = 'a',b;
    printf("%c,", ++a);
    printf("%c\n", b = a++);
    return 0;
}

A. b,b B. b,c C. a,b D. a,c

解析:正确答案是A选项。

(1)a 初始为 ' a '(ASCII 97);

(2)++a:先自增,a 变为 ' b '(98),输出 ' b ';

(3)b = a++:先将a(当前为' b ')赋值给b,然后a自增为' c '。所以b为' b ',输出' b ';

(4)因此输出"b,b"。

1.5 题目5

**题干:**以下逗号表达式的值为( )

cpp 复制代码
(x = 4 * 5 , x * 5),x + 5;

A. 25 B. 20 C. 100 D. 45

解析:正确答案是A选项。

逗号表达式的值是最后一个子表达式的值。

(1)先计算x = 4 * 5(即20),所以x = 20。

(2)然后计算x * 5(即100),但逗号表达式的值取最后一个,即 x * 5 为100?但注意整个表达式是 ((x = 4 * 5, x * 5) , x + 5),所以最外层逗号的值是x + 5。

(3)具体:内层 (x =20, x * 5) 的值为100(但x仍为20),然后外层逗号表达式值为 x + 5 = 25。

实际上,表达式是(x = 4 * 5,x * 5), x + 5,整个逗号表达式的值是最后一个(即x + 5)。
所以值为 20 + 5 = 25。

选择题答案如下:

1.1 D

1.2 A

1.3 A

1.4 A

1.5 A

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

二、两道算法题

2.1 自除数

力扣链接:728. 自除数

力扣题解链接:逐位分解法解决【自除数】问题

题目描述:

2.1.1 题目理解

检查数字是否能被其每一位非零数字整除的数学问题。

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

2.1.2 思路

算法说明:

1、辅助函数 isSelfDividing

(1)检查一个数是否是自除数。

(2)逐位取出数字,检查------

1)如果数字为0,直接返回false(自除数不允许包含0);

2)如果原数不能被该位数字整除,返回false。

(3)所有位都满足条件则返回true。

2、主函数

(1)遍历从left到right的所有数字;

(2)对每个数字调用辅助函数检查是否为自除数;

(3)将满足条件的数字加入结果集。

代码演示:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

bool isSelfDividing(int num) {
    int temp = num;
    while (temp > 0) {
        int digit = temp % 10;
        if (digit == 0 || num % digit != 0) {
            return false;
        }
        temp /= 10;
    }
    return true;
}

int* selfDividingNumbers(int left, int right, int* returnSize) {
    int* result = (int*)malloc((right - left + 1) * sizeof(int));
    int count = 0;
    
    for (int i = left; i <= right; i++) 
    {
        if (isSelfDividing(i)) 
        {
            result[count++] = i;
        }
    }
    
    *returnSize = count;
    return result;
}

时间复杂度O(n*d)

空间复杂度O(n)

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

代码演示:

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    vector<int> selfDividingNumbers(int left, int right) 
    {
        vector<int> result;
        for (int i = left; i <= right; i++) 
        {
            if (isSelfDividing(i)) 
            {
                result.push_back(i);
            }
        }
        return result;
    }
    
private:
    bool isSelfDividing(int num) 
    {
        int temp = num;
        while (temp > 0) 
        {
            int digit = temp % 10;
            if (digit == 0 || num % digit != 0) 
            {
                return false;
            }
            temp /= 10;
        }
        return true;
    }
};

时间复杂度: O(n*d)****,空间复杂度: O(n)****。

时间复杂度O(n × d),其中n是数字范围大小,d是数字的平均位数;
空间复杂度O(n)(存储结果)。

两种实现的核心逻辑相同,只是C++使用了vector容器,而C语言需要手动管理内存。

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

2.2 除自身以外数组的乘积

力扣链接:238. 除自身以外数组的乘积

力扣题解链接:前缀后缀乘积法解决【除自身以外数组的乘积】

题目描述:

2.2.1 题目理解

使用前缀和后缀乘积在O(n)时间内计算数组中每个元素除自身外所有元素乘积的算法问题。

2.2.2 思路

思路 :使用前缀乘积和后缀乘积的方法,避免使用除法。

1、第一次遍历(从左到右)

(1)计算每个元素左边所有元素的乘积;

(2)answer[ i ] = nums[0] * nums[1] * nums[2] ... * nums[i - 1]。

2、第二次遍历(从右到左)

(1)维护一个变量 right 表示当前元素右边所有元素的乘积;

(2)将左边的乘积与右边的乘积相乘得到最终结果;

(3)answer[ i ] = left_product * right_product。

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

代码演示:

cpp 复制代码
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
bool isSelfDividing(int num) {
    int temp = num;
    while (temp > 0) 
    {
        int digit = temp % 10;
        if (digit == 0 || num % digit != 0) 
        {
            return false;
        }
        temp /= 10;
    }
    return true;
}

int* selfDividingNumbers(int left, int right, int* returnSize) {
    int* result = (int*)malloc((right - left + 1) * sizeof(int));
    int count = 0;
    
    for (int i = left; i <= right; i++) 
    {
        if (isSelfDividing(i)) 
        {
            result[count++] = i;
        }
    }
    
    *returnSize = count;
    return result;
}

时间复杂度O(n)

空间复杂度O(1)
(1)时间复杂度O(n) ------ 进行两次遍历;
(2)空间复杂度O(1) ------ 除了结果数组外只使用了常数空间(C++版本为**O(n)**存储结果,但题目要求如此)。

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

代码演示:

cpp 复制代码
class Solution {
public:
    vector<int> selfDividingNumbers(int left, int right)
    {
        vector<int> result;
        for (int i = left; i <= right; i++)
        {
            if (isSelfDividing(i))
            {
                result.push_back(i);
            }
        }
        return result;
    }

private:
    bool isSelfDividing(int num)
    {
        int temp = num;
        while (temp > 0)
        {
            int digit = temp % 10;
            if (digit == 0 || num % digit != 0)
            {
                return false;
            }
            temp /= 10;
        }
        return true;
    }
};

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

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


结尾

本文内容到这里就全部结束了,希望大家练习一下这几道题目,这些基础题最好完全掌握!

往期回顾:

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

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

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

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

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

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

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

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

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