选择题
01
以下for循环的执行次数是()
for(int x = 0, y = 0; (y = 123) && (x < 4); x++);
A 是无限循环
B 循环次数不定
C 4次
D 3次
解题思路:
我们直接来看本道题中最关键的部分:(y = 123) && (x < 4)
。
-
(y = 123)
这是一个赋值表达式,它将y
的值设置为123,并返回赋值的结果,即 123。在 C++ 中,赋值表达式的结果是被赋的值,因此这里的(y = 123)
的结果是 123。123 表达的就是逻辑真。因此&&
左侧的结果一定为true
。 -
题目就转换成了这个样子:
for(int x = 0, y = 0; (x < 4); x++);
显然这个 for
循环执行的次数就是 4 次啦!
答案:C
知识点总结:
- 赋值表达式的返回结果就是等号右侧的值。
02
以下程序的运行结果是()
#include <stdio.h> int main(void) { printf("%s , %5.3s\n", "computer", "computer"); return 0; }
A computer , puter
B computer , com
C computer , computer
D computer , compu.ter
解题思路:
我们直接来看本题中最关键的部分:
%5.3s
:首先%s
是格式化输出字符串的,其中的 5 表示输出字符串的宽度为 5。.
后面的 3 表示从输出字符串的左边截取 3 个字符,并且是右对齐,要求字符串的宽度是 5,因此还要补上空格。- 综上所述,最后的输出结果就是:" com",“com” 前面有两个空格。
答案:B
知识点总结:
%m.ns
:- m:表示输出字符串的宽度。
- n:从左边开始截取目标字符串 n 个字符,并且是右对齐的,如果
n < m
就会补上空格。 - 当
字符串长度 > n > m
时,m
会失效。 - 当
n > 字符串长度 > m
时,m
失效。 - 当
n > 字符串长度,字符串长度 < m
就要补上空格。
打印这个 ^
是为了看出来空格哈!
03
下列 main() 函数执行后的结果为()
int func() { int i, j, k = 0; for(i = 0, j = -1;j = 0;i++, j++) { k++; } return k; } int main() { cout << (func()); return 0; }
A -1
B 0
C 1
D 2
解题思路:
- 在
for
循环的循环条件中,j = 0
是一个赋值表达式,返回的结果就是给 j 赋值的 0,而 0 对应的逻辑结果应该为false
。因此,func
函数中for
循环的循环体不会执行。 func
函数的返回值就是在func
函数中为k
赋的初值 0。
答案:B
知识点总结:
- 赋值表达式的返回结果就是等号右侧的值。
04
下面程序输出是什么?
#include <stdio.h> int main() { int a = 1, b = 2, c = 3, d = 0; if (a == 1 && b++ == 2) if (b != 2 || c-- != 3) printf("%d,%d,%d\n", a, b, c); else printf("%d,%d,%d\n", a, b, c); else printf("%d,%d,%d\n", a, b, c); return 0; }
A 1,2,3
B 1,3,2
C 3,2,1
D 1,3,3
解题思路:
if (a == 1 && b++ == 2)
:a 的初值为 1,与 1 判断相等结果显然为true
;b++
是后置加加,会先将 b 的值参与运算,然后再做加加,b 的初值为 2,与 2 判断相等结果也是true
。逻辑操作符&&
要两侧的表达式均为true
才会返回true
。因此这个if
条件判断是满足条件的。if (b != 2 || c-- != 3)
:在上面的if
判断中已经将 b 的值修改成了 3,此时再将 b 与 2 做不等判断,得到的结果显然是true
;c--
是后置减减,会先将 c 的值参与运算,然后在做减减,c 的初值为 3,与 3 判断不相等,结果显然为false
。逻辑操作符||
当两侧的表达式有一个为true
时就会返回true
。因此这个if
条件判断也是满足的。- 那么最后输出的结果是:1,3,2 吗?不是哦!在
if (b != 2 || c-- != 3)
中,第一个逻辑表达式b != 2
得到的结果已经是true
了,if
条件判断的结果也就出来了,编译器并不会进行c-- != 3
的运算,c 也就没有被减减。
答案:D
知识点总结:
- 前置加加/前置减减:先将变量加一,再参与表达式的运算。
- 后置加加/后置减减:先将变量减一,再参与表达式的运算。
- &&:两侧的表达式结果均为
true
结果才为true
。其余情况均为false
。 - ||:两侧的表达式结果均为
false
结果才为false
。其余情况均为true
。 if
条件判断中从左向右进行逻辑运算,如果结果已经能确定if
表达式整体的结果,就不会继续向右进行逻辑运算。
05
若有定义语句: int a=10 ; double b=3.14 ; 则表达式 ‘A’+a+b 值的类型是()
A char
B int
C double
D float
解题思路:
本体考察的是 C++ 的类型转换,原表达式中参与运算的类型有:char
,int
,double
。根据C++中的运算规则,‘A’ 是一个字符常量,而 a 是一个整数,这里使用整数提升将字符 ‘A’ 转换为其对应的 ASCII 码值,然后与整数a相加,得到一个整数值。接着,整数值与 double
类型的 b 相加,整数值会被自动转换为 double
类型,因为C++中的类型提升规则会使表达式中的最低精度类型向最高精度类型提升。因此,表达式的最终结果将是一个double类型的值。
答案:C
知识点总结:
06
在 int
p[][4]
= {{1}, {3, 2}, {4, 5, 6}, {0}}; 中,p[1][2]
的值是()A 1
B 0
C 6
D 2
解题思路:
p[][4]
是一个行数不确定,列数为 4 的二维数组,在对 p[][4]
进行初始化的时候,我们初始化了 4 行数据,编译器会自动将第一个方括号中填上数字 4。因此原数组是一个 4 行 4 列的数组。p[1][2]
表示第 2 行,第 3 列的数据,在初始化的时候,并没有显示指定他的值,编译器对于这种不完全初始化数组的行为,会将其余的位置全部用 0 来填充。
答案:B
知识点总结:
- 二维数组的第一个方括号可以不填,根据初始化来确定行数,但是第二个方括号不可以不填。
- 数组部分初始化会将为显示初始化的部分设置为 0。
07
选择表达式 11 | 10 的结果(本题数值均为十进制)()
A 11
B 10
C 8
D 2
解题思路:
本题考察进制转换和或位运算的知识。按位或:二进制位有 1 则为 1。
1011 按位或 1010 − − − − − − − 1011 \qquad\qquad1011\\按位或\,\,\,\,\,\,1010\\-------\\\qquad\qquad1011 1011按位或1010−−−−−−−1011
答案:A
知识点总结:
- 按位或
|
:有 1 结果为 1,全 0 结果为 0。 - 按位与
&
:有 0 结果为 0,全 1 结果为 1。 - 按位异或
^
:相同为 0,相异为 1。或者是无进位加法。
08
fun(21)运行结果是()
int fun(int a) { a^ = (1<<5) - 1; return a; }
A 10
B 5
C 3
D 8
解题思路:
1 << 5
其实就是
2
5
2^{5}
25 哈,
2
5
−
1
2^{5}-1
25−1 对应的二进制为:11111
,然后
(
21
)
10
按位异或
(
11111
)
2
(21)_{10}\,按位异或\,(11111)_2\,\,
(21)10按位异或(11111)2。21 对应的二进制:10101
。
10101 按位异或 11111 − − − − − − − − − 01010 \qquad\qquad\qquad10101\\按位异或\qquad11111\\---------\\\qquad\qquad\qquad01010 10101按位异或11111−−−−−−−−−01010
最终结果就是 10 哈。
答案:A
知识点总结:
- 左移运算符
<<
:二进制位整体向左移动一位,右侧补零。左移相当于乘以 2。 - 右移运算符
>>
:符号位不变,二进制位整体向右移动一位。右移相当于整除 2。
09
若有定义语句:int year=1009,*p=&year;以下不能使变量 year 中的值增至 1010 的语句是()
A
*p += 1
B(*p)++
C
++(*p)
D*p++
解题思路:
题目中定义了一个整型变量 year
赋值为 1009,定义了一个指针变量 p
赋值为 year
的地址。这就意味着,我们对指针变量 p
解引用之后就能找到 year
变量(就是定位到 year
变量的内存),即对 *p
的操作其实就是对 year
变量的操作。
- A 选项
*p
就是year
显然正确。 - B 选项也正确。
- C 选项同样正确。
- D 选项中
++
运算符的优先级高于*
运算符。也就是说,先对 p 进行了加加,再解引用。显然这是内存非法访问了。因为p
是一个int
类型的指针,加一就会跳过一个int
类型空间,也就是四个字节的空间。
答案:D
知识点总结:
- 指针其实就是变量的地址,指针解引用就能找到这个变量在内存中的地址,也就是找到了这个变量本身,指针解引用之后的操作就是对该变量本身在进行操作。
- 注意运算符的优先级问题。
10
下面关于 “指针” 的描述不正确的是()
A 当使用 free 释放掉一个指针内容后, 指针变量的值被置为 NULL
B 32 位系统下任何类型指针的长度都是 4 个字节
C 指针的数据类型声明的是指针实际指向内容的数据类型
D 野指针是指向未分配或者已经释放的内存地址
解题思路:
- A 选项:
free()
操作是用于释放动态分配的内存的函数,它的本质是告诉内存管理系统,某个特定地址所指向的内存块现在可以被重新使用,在调用free
函数后,原指针变量并不会被置为NULL
。所以在释放内存后,最好不要再访问这些已释放的内存,以免引发未定义行为或其他问题。A 选项错误。 - B 选项:32 为操作系统下,地址总线 32 根。也就是说我们对内存进行寻址时只需要使用 32 个比特位就行啦!32 个比特位就是 4 字节,B 选项正确。
- C 选项:C 肯定时正确的。
- D 选项:野指针也叫悬空指针,指向未分配或者已经释放的内存地址,是正确的。
答案:A
知识点总结:
见解题思路。
编程题
01
牛牛举办了一次编程比赛,参加比赛的有 3 n 3n 3n 个选手,每个选手都有一个水平值 a_i。现在要将这些选手进行组队,一共组成 n 个队伍,即每个队伍 3 人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。
例如:
一个队伍三个队员的水平值分别是3,3,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是3,2,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是1,5,2.那么队伍的水平值是2
为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。
如样例所示:
如果牛牛把 6 个队员划分到两个队伍
如果方案为:
team1:{1,2,5}, team2:{5,5,8}, 这时候水平值总和为7.
而如果方案为:
team1:{2,5,8}, team2:{1,5,5}, 这时候水平值总和为10.
没有比总和为 10 更大的方案,所以输出10.输入描述:
输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
第二行包括3*n个整数a_i(1 ≤ a_i ≤ 10^9),表示每个参赛选手的水平值.输出描述
输出一个整数表示所有队伍的水平值总和最大值.示例1:
输入
2
5 2 8 5 1 5
输出
10
题目解析:
- 队伍的水平值等于该队伍队员中第二高水平值,为了所有队伍的水平值总和最大的解法,也就是说每个队伍的第二个值是尽可能大的值。所以实际值把最大值放到最右边,最小是放到最左边。
解题思路:
-
本题的主要思路是贪心算法,贪心算法其实很简单,就是每次选值时都选当前能看到的局部最解忧,所以这里的贪心就是保证每组的第二个值取到能选择的最大值就可以,我们每次尽量取最大,但是最大的数不可能是中位数,所以退而求其次,取 每组中第二大的
-
例如 现在排序后 有 1 2 5 5 8 9 ,那么分组为1 8 9 和 2 5 5
-
不难推出我们要选择的第二大的元素为: a r r [ a r r . s i z e ( ) − 2 ∗ ( i + 1 ) ] arr[arr.size() - 2 * (i+1)] arr[arr.size()−2∗(i+1)]。
完整代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
long long sum = 0;
vector<int> a;
a.resize(3 * n);
for (int i = 0; i < (3 * n); i++)
{
cin >> a[i];
}
std::sort(a.begin(), a.end());
for (int i = 0; i < n; i++)
{
sum += a[a.size() - (2 * (i + 1))];
}
cout << sum << endl;
}
}
02
输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入 “They are students.” 和 “aeiou”,则删除之后的第一个字符串变成 “Thy r stdnts.”
输入描述:
每个测试输入包含2个字符串输出描述:
输出删除后的字符串示例1:
输入
They are students.
aeiou
输出
Thy r stdnts.
解题思路:
本题如果使用传统的暴力查找方式,如判断第一个串的字符是否在第二个串中,在再挪动字符删除这个字符的方式,效率为
O
(
N
2
)
\,O(N^2)\,
O(N2),效率太低,很难让人满意。
- 将第二个字符串的字符都映射到一个
hashtable
数组中,用来判断一个字符在这个字符串。 - 判断一个字符在第二个字符串,不要使用删除,这样效率太低,因为每次删除都伴随数据挪动。这里可
以考虑使用将不在字符添加到一个新字符串,最后返回新新字符串。
完整代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 注意这里不能使用cin接收,因为cin遇到空格就结束了。
// oj中IO输入字符串最好使用getline。
string str1, str2;
// cin>>str1;
// cin>>str2;
getline(cin, str1);
getline(cin, str2);
// 使用哈希映射思想先str2统计字符出现的次数
int hashtable[256] = {0};
for (size_t i = 0; i < str2.size(); ++i)
{
hashtable[str2[i]]++;
}
// 遍历str1,str1[i]映射hashtable对应位置为0,则表示这个字符在
// str2中没有出现过,则将他+=到ret。注意这里最好不要str1.erases(i)
// 因为边遍历,边erase,容易出错。
string ret;
for (size_t i = 0; i < str1.size(); ++i)
}