题目
一、选择题
1、下列程序的输出是()
#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
p 是一个指针数组, p[i] = &a[i*3] 相当于是把数组 a 每 3 个一组分开并把每组的首地址存在 p 数组,此时 p 类似一个 4 行 3 列的 二维数组,p[3][2] 就是 4 行第 3 个元素 12
2、二维数组X按行顺序存储,其中每个元素占1个存储单元。若 X[4][4] 的存储地址为 Oxf8b82140 , X[9][9] 的存 储地址为 Oxf8b8221c ,则 X[7][7] 的存储地址为( )
A: Oxf8b821c4
B: Oxf8b821a6
C: Oxf8b82198
D: Oxf8b821c0
答案解析
正确答案: A
假设每行有 n 个元素:那 x[9][9] 元素的地址 - x[4][4] 元素的地址 = 0x21c-0x140=5n+5(21c 和 140 是地址末三位的十六进制数) ,这里 n 是 43 ,假设 x[7][7] 的地址是 z,x[7][7] 元素的地址 - x[4][4] 元素的地址 = z-0x140 = 3n+3 , z = 3n+3+140 = 3*43+3+0x140 = 0x84+0x140 = 0x1c4,看地址的尾数,选择 A
3、以下哪个选项可以正确描述 sizeof(double) ( )
A: 一个整型表达式
B: 一个双精度型表达式
C: 一个不合法的表达式
D: 一种函数调用
答案解析
正确答案: A
sizeof 是 C 语言中的一个操作符,不是函数调用,简单的说其作用就是返回一个对象或者类型所占的内存字节数,结果是无符 号整数,因此可以把它看作是整型表达式。所以选择A
4、下列代码运行后的结果是什么( )
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
变量 a 里边存的是字符 'a' ,第一次输出先加加再输出,输出的是 'b'; 第二次输出的时候, a 先赋值再加加,赋值给 b 的就是 a 原来的值,输出b 的时候的还是 'b'
5、以下逗号表达式的值为( )
(x= 4 * 5 , x * 5) , x + 5;
A: 25
B: 20
C: 100
D: 45
答案解析
正确答案: A
逗号表达式是从前到后依次计算子表达式,而其结果是最后一项的值,此题去掉括号后的表达式,和原表达式是等价的,先计算4*5 并赋值给 x , x 变为 20 ,中间 x*5 并没有改变 x 的值,最后一项 x+5 值是 25 ,也就是整个表达式的值
二、编程题
1 :LeetCode 728. 自除数
自除数 是指可以被它包含的每一位数除尽的数。
例如,128 是一个自除数,因为 128 % 1 == 0,128 % 2 == 0,128 % 8 == 0。
还有,自除数不允许包含 0 。
给定上边界和下边界数字,输出一个列表,列表的元素是边界(含边界)内所有的自除数。
示例 1
输入:
上边界left = 1, 下边界right = 22
输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]
注意:
每个输入参数的边界满足 1 <= left <= right <= 10000。
【答案解析】
自除数的判断,数字的每一位都能被源数字整除,有了这个规则,咱们只需要循环获取一个数字的每一位,然后与源数字取模判断是否为 0 ,如果中间有任意一次不为 0 ,则表示不是自除数。
接下来,只需要遍历数组中的每个元素,判断是否是自除数即可,如果是则加入到返回数组中。
1.int* selfDividingNumbers(int left, int right, int* returnSize) { int* ret = (int*)calloc(1000, sizeof(int));//动态申请足够大的空间用于存放返回的自除数 *returnSize = 0; for (int i = left; i <= right; i++) { int num = i; while(num) { int remainder = num % 10;//计算余数 if (remainder == 0 || (i % remainder) != 0) {//判断i自身与余数取模是否为0 break; } num /= 10; } //如果num==0表示通过了每一位数的取模判断,则i就是自除数 if (num == 0) ret[(*returnSize)++] = i; } return ret; }
2:LeetCode238. 除自身以外数组的乘积
238. 除自身以外数组的乘积 - 力扣(LeetCode)
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例
输入: [1,2,3,4]
输出: [24,12,8,6]
提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都
在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
【答案解析】
暴力不考虑其他的因素的话,将所有数据乘积起来,然后遍历数组除以当前位置数据即可。
更优解法:将乘积分为两次进行,第一次先将每个位置左边的数据乘积计算出来放到返回数组中,后边第二次循环将对应位置右边的数据乘积计算出来与返回数组对应位置的左半边乘积相乘得到结果。
示例: 一个数组 int nums[] = {2, 3, 4}。
int left = 1, right = 1;
计算左侧乘积:
第 0 个元素的左边乘积, arr[0] = left 然后计算第 1 位左侧乘积 left*=nums[0] -> left = 1*2
第 1 个元素的左边乘积, arr[1] = left 然后计算第 2 位左侧乘积 left*=nums[1] -> left = 1*2*3
第 2 个元素的左边乘积, arr[2] = left 然后计算第 3 位左侧乘积
已经没必要了,因为第 2 元素是末尾元素了
一次循环完毕后,返回数组中每个元素存储的都是自己左侧元素的乘积。 arr[] 中的值: [1, 2, 6]
计算右侧乘积:
第 2 个元素的右边乘积, arr[2] *= right 然后计算第 1 位右侧乘积
right*=nums[2] -> right =1*4
第 1 个元素的右边乘积, arr[1] *= right 然后计算第 0 位右侧乘积
right*=nums[1] -> right =1*4*3
第 0 个元素的右边乘积, arr[0] *= right 然后计算第 -1 位右侧乘积 -1 位已经不需要计算了
循环完毕后,返回数组中的每个元素都是其他元素的乘积了 arr[2]*=1 ; arr[1]*=4; arr[0]*=12
int* productExceptSelf(int* nums, int numsSize, int* returnSize) { int* ret = (int*)malloc(numsSize * sizeof(int)); *returnSize = numsSize; int left = 1, right = 1; //第一次循环,将当前位置左边的数字乘积填入返回数组中 for (int i = 0; i < numsSize; i++) { ret[i] = left;// 1 nums[0] nums[0]*nums[1] num[0]*nums[1]*nums[2] .... left *= nums[i]; } //第二次循环,对于返回数组的元素从后往前进行,每次乘以右边元素的乘积 for (int i = numsSize - 1; i >= 0; i--) { ret[i] *= right; //最后一个成绩不需要乘以最后元素,乘以1就行 right *= nums[i]; //right变化:1 nums[end] nums[end]*nums[end-1] ..... } return ret; }