目录
选择题
1、设变量已正确定义,以下不能统计出一行中输入字符个数(不包含回车符)的程序段是
- A
n=0;while(ch=getchar()!='\n')n++;
- B
n=0;while(getchar()!='\n')n++;
- C
for(n=0;getchar()!='\n';n++);
- D
n=0;for(ch=getchar();ch!='\n';n++);
答案:D
解析:
对于for
循环,其中第一项初始化表达式只执行一次,因此ch
只从输入流中取一个字符,之后就再不会取字符,因此会死循环
2、运行以下程序后,如果从键盘上输入65 14<回车>,则输出结果为
int main()
{
int m, n;
printf("Enter m,n;");
scanf("%d%d", &m,&n);
while (m!=n) //1
{
while(m>n) m=m-n; //2
while(n>m) n=n-m; //3
}
printf("m=%d\n",m);
return 0;
}
- A 3
- B 2
- C 1
- D 0
答案:C
解析:
此题技巧是耐心,考查while
循环和循环嵌套的理解,初值m=65,n=14;
循环1判断m!=n
为真,来到循环2判断m>n
为真,执行m=m-n;
直到m=9,n=14;
循环2结束来到循环3判断n>m
为真,执行n=n-m;
直到m=9,n=5;
循环3结束回到循环1,如此往复直到m==n
时,循环结束
3、若运行以下程序时,从键盘输入ADescriptor<回车>,则下面程序的运行结果是
#include <stdio.h>
int main()
{
char c;
int v0=0,v1=0,v2=0;
do
{
switch(c=getchar())
{
case'a':case'A':
case'e':case'E':
case'i':case'I':
case'o':case'O':
case'u':case'U':v1 += 1;
default:v0+= 1;v2+=1;
}
}while(c!='\n');
printf("v0=%d,v1=%d,v2=%d\n",v0,v1,v2);
return 0;
}
- A
v0=7,v1=4,v2=7
- B
v0=8,v1=4,V2=8
- C
v0=11,v1=4,v2=11
- D
v0=12,v1=4,v2=12
答案:D
解析:
观察循环,当输入AEIOU或者aeiou都会进入case
中,否则进入default
中,对于输入ADescriptor<回车>中,满足case
条件的有Aeio,故有v1
需要加四次,即0+1+1+1+1
最后结果为4,剩余的不满足case
条件的有Dscrptr,加七次结果为7,所以答案选A,到这就结束了?选A的再仔细看本题,本题最后一个case
到default
为止没有出现一个break
语句,所以在一开始遇到Aeio时,也会进入default
语句,所以v0
和v2
自始至终都会执行,故执行4+7次,所以选C,真的?别忘记了还有最后一个'\n'
,在循环中读取输入时,'\n'
才会存入变量c中,而在读取到'\n'
时,已经进入循环,所以'\n'
会参与运算,这是最后一次运算,之后因为c
是'\n'
,退出循环,此时v0
为12,v2
为12,v1
为4,所以正确答案为D
4、(多选题)如下函数是求两个int数字最大公约数的,指出其中存在的问题
int gcd(char x,char y)
{
int min = x < y ? x : y;
for (min = 0; min > 0; min--)
if (x % min = 0 && y % min = 0)
return min;
}
- A 参数类型不对
- B 循环变量min初值不对
- C 判断等于的符号不对
- D 返回类型不对
答案:ABC
解析:
1.函数实参是int
,形参用char
不对,会发生截断丢失数据;2.min
在for
循环开始时更新为0,不再是两个形参中的较小值;3.判断是否整除的时候误将==
写成=
赋值运算符;4.函数最终要返回一个int
值,返回值类型没问题,但是这里要强调一个选项中没写出的问题,如果是牛客网上的题,会报编译错误,说该函数不是在所有情况下都有返回值,只有在if条件成立的情况下有返回值,一般在vs上这种情况能通过,编译器会给一个默认的返回值
5、执行下面的程序段,语句3的执行次数为
for(i = 0; i <= n-1; i++) // (1)
for(j = n; j > i; j--) // (2)
state; // (3)
- A n(n+2)/2
- B (n-1)(n+2)/2
- C n(n+1)/2
- D (n-1)(n+2)
答案:C
解析:
外循环有n次,当i=0
,内循环为n
次,当i=1
,内循环为n-1
次,当i=2
时,内循环为n-2
次,以此类推,总次数为n+(n-1)+(n-2)+......+2+1,就是个等差数列,等于n(n+1)/2
编程题
错误的集合
题目链接:645. 错误的集合 - 力扣(LeetCode)
集合s
包含从1
到n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组nums
代表了集合S
发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
思路解析:
本题可以考虑两个思路:
- 数学方法
先对数组进行由小到大排序,因为缺失的数值介于前一个数值和后一个数值中间,并且在不缺失的情况下相邻两个数值差值为1,如果有数值重复,那么重复的数值和下一个不同的数值之间的差值为2,所以需要记录前一个数值存储在变量prev
中
注意
prev
初始化为0,因为存在数组中缺失的数值为1,而如果数组中只有两个元素,那么最大值为2,此时在数组中找不到一个数值使得重复的数值和下一个的不同两个数值差值为2
先找到重复的数值,即下一个元素和当前元素cur
相同时,存储当前数值cur
到新数组的第一个元素的位置,更新prev
为当前的重复数值
再继续循环,若当前数值与prev
中存的数值差值大于1,说明此时已经找完了重复元素,因为不缺失数值时相邻两个数值差值为1,而此时prev
中的数值和当前数值差值大于1,说明prev
中的数值+1即为缺失的数值,将prev + 1
存储新数组的第二个位置后更新prev
当走完该循环时,还需要注意一个问题:如果缺失的数值是数组的最大值(例如122,缺失3),此时需要单独判断,因为不存在一个数值使得后一个元素和当前元素差值为2,所以当数组的最后一个元素不等于数组的大小时,此时即为缺失数组最大值
- 异或运算
本题同样可以考虑异或运算对重复数值和缺失数值分堆来求解,但是直接在原数组中对元素进行异或不可取
考虑到以下规律:
在原数组中,观察到重复的数值和缺失的数值都出现偶数次,而其他数值均出现奇数次,如果构造一个1~n的不缺数的数组和原数组组合,那么重复的数值和缺失的数值都出现奇数次,而其他数值均出现偶数次,在异或运算中,相同的两个数异或可以得到a^a = 0
,而a^0 = a
,故此时对构造的数组和原数组进行整体异或可以得到重复的数值和缺失的数值异或的结果值
故采用先构造一个数组和原数组进行整体异或,得到一个返回值ret即为重复数值和缺失数值的异或结果,因为异或运算的本质是找出两个操作数二进制位不同的位置,所以计算结果的二进制位为1的位置即为重复的数值和缺失的数值相异的位置,此时可以采用循环结合右移运算符找到相异的位置(二进制位数的下标,例如1和5中倒数第三位不同),但是本次将采用返回值与返回值的负数形式相与计算得出最低的不同位,即int position = ret & (-ret);
,但是注意该语句计算的不是不同的下标位置,而是具体的值,但是该值的二进制值中为1的位置为对应的两个数不同的位置
找到最低的不同位之后,通过原数组和构造的数组中的元素与不同位的数值相与运算得到结果为0和不为0的数值,将二者分堆即可分出重复的数值和缺失的数值(因为重复的数值和缺失的数值不相同,所以二者不可能在同一堆中)
最后,因为暂时不知道重复的数值和缺失的数值具体所在的位置,所以需要遍历原数组确定重复的数值存储在新数组的第一个元素的位置,缺失的数值则放置到新数组的第二个位置
参考代码
排序+调整(数学方法)
/*
* @lc app=leetcode.cn id=645 lang=c
*
* [645] 错误的集合
*/
// @lc code=start
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int cmp(const void *p1, const void *p2)
{
return (*(int *)p1 - *(int *)p2);
}
int *findErrorNums(int *nums, int numsSize, int *returnSize)
{
// 对已知数组进行从小到大排序
qsort(nums, numsSize, sizeof(int), cmp);
int *p = (int *)malloc(sizeof(int) * 2);
// 定义变量记录数组中上一个元素的位置,便于计算缺失的数值
int prev = 0;
// 遍历数组
// 1.如果找出的元素与prev中的值差值大于1,说明当前缺失的元素即为prev当前值的后一位数值
// 2.如果找出的元素与prev中的值相等,说明遇到了相同元素
for (int i = 0; i < numsSize; i++)
{
int cur = nums[i]; // 遍历数组
if (cur == prev)
{
// 如果相同则表示遇到相同元素
p[0] = cur; // 记录相同元素
}
else if (cur - prev > 1)
{
// 如果不同且满足二者差值大于1时,则表示中间有缺失元素,并且缺失元素即为prev当前大小+1
p[1] = prev + 1;
}
prev = cur; // 更新prev
}
// 存在一种情况:数组中最后一个数值小于数组长度
if (nums[numsSize - 1] != numsSize)
{
// 数组中缺失的数值为数组的最大值
p[1] = numsSize;
}
*returnSize = 2;
return p;
}
// @lc code=end
异或运算
/*
* @lc app=leetcode.cn id=645 lang=c
*
* [645] 错误的集合
*/
// @lc code=start
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int *findErrorNums(int *nums, int numsSize, int *returnSize)
{
int *p = (int *)malloc(sizeof(int) * 2);
int ret = 0;
// 构造一个1-n的不缺数值的数组与原来的数组进行整体异或
for (int i = 1; i <= numsSize; i++)
{
// 将不缺数的数组进行整体异或
ret ^= i;
// 将缺数的数组与不缺数的数组进行整体异或
ret ^= nums[i - 1];
}
// 当前ret中存储的值为重复数值和缺失数值异或的结果
// 找出重复数值和缺失数值二者最低的不同位
// 可以代替原来的移位找最低不同位算法
int position = ret & (-ret);
// 定义两个数值分别存储重复数值和缺失的数值
int num1 = 0;
int num2 = 0;
// 根据最低的不同二进制位对原数组进行分组
for (int i = 0; i < numsSize; i++)
{
if ((nums[i] & position) == 0)
{
num1 ^= nums[i];
}
else
{
num2 ^= nums[i];
}
}
// 根据最低的不同二进制位对构造数组进行分组
for (int i = 1; i <= numsSize; i++)
{
if ((i & position) == 0)
{
num1 ^= i;
}
else
{
num2 ^= i;
}
}
// 因为当前不确定重复的数值和缺失的数值在num1和还是num2,所以需要与原数组进行再一次的比较找出重复数值,剩下的就是缺失的数值
*returnSize = 2;
for (int i = 0; i < numsSize; i++)
{
if (nums[i] == num1)
{
p[0] = num1;
p[1] = num2;
return p;
}
}
// 如果已经确定则直接返回
p[0] = num2;
p[1] = num1;
return p;
}
// @lc code=end
密码检查
题目链接:密码检查_牛客题霸_牛客网 (nowcoder.com)
描述
小明同学最近开发了一个网站,在用户注册账户的时候,需要设置账户的密码,为了加强账户的安全性,小明对密码强度有一定要求:
1. 密码只能由大写字母,小写字母,数字构成;
2. 密码不能以数字开头;
3. 密码中至少出现大写字母,小写字母和数字这三种字符类型中的两种;
4. 密码长度至少为8
现在小明受到了n个密码,他想请你写程序判断这些密码中哪些是合适的,哪些是不合法的。
输入描述:
输入一个数n,接下来有n(n≤100)行,每行一个字符串,表示一个密码,输入保证字符串中只出现大写字母,小写字母和数字,字符串长度不超过100。
输出描述:
输入n行,如果密码合法,输出YES,不合法输出NO
思路解析:
本题难度不大,需要对要求进行代码形式转化,并且需要注意因为不只有一组输入,所以要处理好多组输入的情况,对于“至少要出现大写字母,小写字母和数字这三种字符中的两种”的条件可以考虑用逻辑条件判断来解决,比如记录大写字母,小写字母和数字三种类型出现的次数,如果满足大写字母出现的次数+小写字母出现的次数+数字出现的次数<2,说明不满足条件。另外,需要注意的地方是,不建议满足条件返回true
,因为满足条件的情况很多,所以确定满足条件的边界外的部分返回false
会更容易处理问题
参考代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int num = 0;
while (scanf("%d", &num) != EOF)
{
//因为不止一组输入,故需要循环控制输入次数
for (int i = 0; i < num; i++)
{
//在函数中定义数组便于每一次输入都存入新数组
char code[101] = { 0 };
scanf("%s", code);
//如果输入完成后,数组总长度小于8,则代表此次密码不行,重新输入新的密码
if (strlen(code) < 8)
{
printf("NO\n");
continue;
}
//如果输入完成后,数组的第一个字符是数字,则重新输入
if (code[0] >= '0' && code[0] <= '9')
{
printf("NO\n");
continue;
}
//判断密码中的各种类型的字符是否满足要求
int digit = 0;
int capital = 0;
int lower = 0;
int wrong = 0;
for (int j = 0; j < strlen(code); j++)
{
if (code[j] >= 'a' && code[j] <= 'z')
{
lower++;
}
else if (code[j] >= 'A' && code[j] <= 'Z')
{
capital++;
}
else if (code[j] >= '0' && code[j] <= '9')
{
digit++;
}
else
{
wrong++;
}
}
//如果出现非法字符就判错重新输入
if (wrong)
{
printf("NO\n");
continue;
}
//如果不满足指定类型的字符的数量判错重新输入
if ((digit > 0) + (capital > 0) + (lower > 0) < 2)
{
printf("NO\n");
continue;
}
else
{
printf("YES\n");
}
}
}
return 0;
}