一、选择题
1、有如下代码,则 *(p[0]+1) 所代表的数组元素是( )
int a[3][2] = {1, 2, 3, 4, 5, 6}, *p[3]; p[0] = a[1];
A: a[0][1]
B: a[1][0]
C: a[1][1]
D: a[1][2]
答案解析
正确答案: C
p 是一个指针数组, p[0] = a[1]; 此处 a[1] 是二维数组的第二行的数组名,数组名表示首元素的地址, a[1] 是 a[1][0] 的地址,所以p[0] 中存储的是第 2 行第 1 个元素的地址, p[0]+1 就是第二行第 2 个元素的地址, *(p[0]+1) 就是第二行第二个元素了。所以C 正确。
2、关于指针下列说法正确的是【多选】( )
A: 任何指针都可以转化为void *
B: void *可以转化为任何指针
C: 指针的大小为8个字节
D: 指针虽然高效、灵活但可能不安全
答案解析
正确答案: ABD
C 选项,指针占几个字节要看平台, 64 位环境下 8 个字节, 32 位环境下 4 个字节
3、以下 scanf 函数调用选项中, 错误的是( )
struct T { char name[20]; int age; int sex; } a[5], *pa=a;
A: scanf("%s",a[0].name);
B: scanf("%d", &pa[0].age);
C: scanf("%d",&(pa->age));
D: scanf("%d", pa->age);
答案解析
正确答案: D
该题考察的是通过 scanf 函数的调用对结构体数据类型进行初始化。 scanf(" 输入控制符 ", 输入参数 ); 功能:将从键盘输入的字符转化为“ 输入控制符 ” 所规定格式的数据,然后存入以输入参数的值为地址的变量中。 scanf 输入时要通过地址找空间,B、 C 用了 & 是正确的。 name 属于字符数组的数组名,相当于数组的首地址, A 正确。单独的 pa->age 可用于输出语句获取值的
形式,用在 scanf 中的时候需要 & 操作符, D 错误
4、如下函数 fun 计算 prod=1*2*3*…*n ,并返回计算结果值。但当 n>12 时,返回值不正确。要找出该程序的错误,正确的调试方法是( )
int fun(int n) { int prod = 1 , i = 0; for(i = 1;i <= n;i++) { prod *= i; } r eturn prod; }
A: 监视变量prod的值,在prod *= i;行处设置断点,然后单步运行,直到发现错误原因
B: 监视变量prod的值,在return prod;行处设置断点,程序中断后,即可发现错误原因
C: 在prod=1;处设置断点,然后在函数调用堆栈中即可发现错误原因
D: 监视变量i的值,在for (i=1; i<=n; i++)行处设置断点,然后单步运行,直到发现错误原因
答案解析
正确答案: A
依题目已知情况,当 n<=12 时结果是正确的,说明是随着参数的变大计算过程中哪里出了问题,故而要在 prod *= i; 处设断点,查看原因。错误原因是数据过大时整型溢出
5、下列给定程序中,函数 fun 的功能是:把形参a所指数组中的奇数按原顺序依次存放到 a[0]、a[1]、a[2]… 中, 把偶数从数组中删除,奇数个数通过函数值返回。 例如,若a所指数组中的数据最初排列为: 9,1,4,2,3,6,5,8,7 ,删除偶数后,a所指数组中的数据为: 9,1,3,5,7 ,返回值为5。请在程序的下画线处填入正 确的内容并将下画线删除,使程序得出正确的结果( )
int fun(int a[], int n) { int i, j; j=0; for (i=0; i<n; i++) if (a[i]%2== _________ ) { a[j]=a[i]; _________; } r eturn _________; }
A: 0 j++ j
B: 1 j++ j+1
C: 0 j++ j+1
D: 1 j++ j
答案解析
正确答案: D
代码实现的思路应该是 arr[i] 是奇数的时候要存储起来,所以第一个空是 1 ,最开始 j 是 0 ,每次找到一个奇数就存储到arr[j]的位置,那接下里 j 需要 +1 ,所以得第二个空是 j++ ,当循环停止的时候, j 其实就是奇数的个数。所以最后返回 j ,第三个空是j 。所以选 D 。
二、编程题
1:KS33 寻找奇数
描述
现在有一个长度为 n 的正整数序列,其中只有一种数值出现了奇数次,其他数值均出现偶数次,请你找出那个出现奇数次的数值。
数据范围:1 \le n \le 2 \times 10^6 \1≤n≤2×106
输入描述
第一行:一个整数n,表示序列的长度。第二行:n个正整数ai,两个数中间以空格隔开。
输出描述
一个数,即在序列中唯一出现奇数次的数值。
示例1
输入:
5
2 1 2 3 1
输出:3
示例2
输入:
1
1
输出:
1
【答案解析】
异或:二进制比特位相同则 0 , 不同则 1.
两个相同的数字异或得到的是 0 , 基于这个思路,这道题对数组中的所有数据进行逐一异或就可以解决得到奇数次的 数字,因为偶数次的数字都被异或成为 0 了,最后单独保留了奇数次的数字。
#include <stdio.h> int main() { int n; while (~scanf("%d", &n)) { int num = 0, tmp = 0; //对每个数字进行异或,出现偶数次的就会异或为0了,而奇数次刚好剩下的就是对应数字 for (int i = 0; i < n; i++) { scanf("%d", &tmp); num ^= tmp; } printf("%d\n", num); } return 0; }
2:NC107 寻找峰值
描述
给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。
1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于
2.假设 nums[-1] = nums[n] = -\infty−∞
3.对于所有有效的 i 都有 nums[i] != nums[i + 1]
4.你可以使用O(logN)的时间复杂度实现此问题吗?
数据范围
1 \le nums.length \le 2\times 10^5 \1≤nums.length≤2×105
-2^{31}<= nums[i] <= 2^{31} - 1−231<=nums[i]<=231−1
如输入[2,4,1,2,7,8,4]时,会形成两个山峰,一个是索引为1,峰值为4的山峰,另一个是索引为5,峰值为8的山峰,如下图所示:
示例1
输入:
[2,4,1,2,7,8,4]
返回值:1
说明:4和8都是峰值元素,返回4的索引1或者8的索引5都可以
示例2
输入:[1,2,3,1]
返回值:2
说明:3 是峰值元素,返回其索引 2
【答案解析】
暴力破解: 遍历数组,从 [1 , n - 1] 号位置开始,哪个位置的数据大于上一个数据和下一个数据即可。
更优思想:二分思想,
中间比右边大,认为从右往左半边递增,则把 right 不断向左靠拢 right=mid ,注意不能是 mid - 1 ,因为这个位置有可能就是峰值点。
直到遇到中间比右边小了,意味着数据开始递降了,则 left 向右偏移, left=mid+1 ; 而一旦 mid+1 位置大于了right ,意味着刚好这个 mid+1 位置,是一个左半边 - 右往左递降,右半边 - 右往左递增的点,就是一个峰值点。
示例:
int findPeakElement(int* nums, int numsLen) { //边界情况处理,1个元素前后都是负无穷 以及 0号位置大于1号位置,-1位置负无穷的情况 if (numsLen == 1 || nums[0] > nums[1]) return 0; //末尾位置数据大于上一个位置数据,而nums[numsLen]负无穷的情况 if (nums[numsLen - 1] > nums[numsLen - 2]) return numsLen - 1; int left = 0, right = numsLen - 1, mid; while (left < right) { mid = left + (right - left) / 2; if (nums[mid] < nums[mid + 1])//中间比右边小,意味着右边肯定有个峰值 left = mid + 1; else //否则在左边包括当前位置肯定有个峰值 right = mid; } return left; }