这是我的第一篇博客,有不对之处请提出。
题解:
一、单项选择题(共 15 题,每题2分,共计30分;每题有且仅有一个正确选项)
1. 在C++中,下面哪个关键字用于声明一个变量,其值不能被修改?( B)
A. unsigned B. const C. static D. mutable
分析:明显,这考察的是基础操作“常量”。
const才是C++中def常量的正确操作。因此选B。
2,八进制数12345670和07654321的和为(D )。
A. 22222221 B. 21111111 C. 22111111 D. 22222211
分析:有些人想到的是把他们都转为十进制,计算。
这有点复杂,其实可以直接用竖式计算,逢8进1。
竖式过程:
12345670
+07654321
__________
?????211
算到这里就可以得出答案了,D。
3. 阅读下述代码,请问修改data的value成员以存储3.14,正确的方式是( A)。
union Data { int num; float value; char symbol; }; union Data data;A. data.value = 3.14; B. value.data = 3.14; C.>struct Node { int data; Node* next; };
现在有一个指向链表头部的指针:Node* head。如果想要在链表中插入一个新节点,其 成员data的值为42,并使新节点成为链表的第一个节点,下面哪个操作是正确的?(A )
A. Node* newNode = new Node; newNode->data = 42; newNode->next = head; head = newNode;
B. Node* newNode = new Node; head->data = 42; newNode->next = head; head = newNode;
C. Node* newNode = new Node; newNode->data = 42; head->next = newNode;
D. Node* newNode = new Node; newNode->data = 42; newNode->next = head;
分析:链表操作题。
还是基础数据结构操作,不过要记得链表的特点,断一个,后面全丢了。
所以要细心一些。
要在链表的头部插入一个新节点,并使其成为第一个节点,我们需要完成以下三个关键步骤:
- 分配内存并创建一个新节点。
- 将新节点的
data赋值为 42。 - 将新节点的
next指针指向当前的头节点(head),然后将头指针head更新为指向这个新节点。
先分析A。
赋值?没问题。指针?没丢掉。头部?修改了。
故此选A。
5. 根节点的高度为1,一棵拥有2023个节点的三叉树高度至少为( C)。
A. 6 B. 7 C. 8 D. 9
分析:手模呀。
题目已知根节点的高度为1,这意味着:
- 第 1 层最多有 30=130=1 个节点
- 第 2 层最多有 31=331=3 个节点
- 第 3 层最多有 32=932=9 个节点
一直算,算到8时,抵达上限,选C。
6. 小明在某一天中依次有七个空闲时间段,他想要选出至少一个空闲时间段来练习唱歌,但 他希望任意两个练习的时间段之间都有至少两个空闲的时间段让他休息。则小明一共有 (B )种选择时间段的方案。
A. 31 B. 18 C. 21 D. 33
分析:这题是经典排列组合题。
由于范围很小(7),所以可以直接硬算。
1. 选 1 个时间段:
随便选哪个都行。
方案数 =7
2. 选 2 个时间段:
- 如果第1个选1:第2个可以选 4, 5, 6, 7 (4种)
- 如果第1个选2:第2个可以选 5, 6, 7 (3种)
- 如果第1个选3:第2个可以选 6, 7 (2种)
- 如果第1个选4:第2个可以选 7 (1种)
- 如果第1个选 5, 6, 7:后面不够放第2个了。
方案数 = 4 + 3 + 2 + 1 =10
3. 选 3 个时间段:
实际上只有一种方案。
所以选择B(10+7+1=18)。
7. 以下关于高精度运算的说法错误的是( C)。
A. 高精度计算主要是用来处理大整数或需要保留多位小数的运算。
B. 大整数除以小整数的处理的步骤可以是,将被除数和除数对齐,从左到右逐位尝试将 除数乘以某个数,通过减法得到新的被除数,并累加商。
C. 高精度乘法的运算时间只与参与运算的两个整数中长度较长者的位数有关。
D. 高精度加法运算的关键在于逐位相加并处理进位。
分析:咋一看,都对?
实际上仔细一点,可以发现C有点问题。
比如:
372
* 23
———
和
1000
* 1
———
我们自己算一下,发现第一个更慢一些。
但第二个最大的位数比第一个大。
这是常识问题,很明显,C是答案。
8. 后缀表达式“6 2 3 +- 3 8 2 / + * 2 ^ 3 +”对应的中缀表达式是( A)
A. ((6- (2 + 3)) * (3 + 8 / 2)) ^ 2 + 3
B. 6- 2 + 3 * 3 + 8 / 2 ^ 2 + 3
C. (6- (2 + 3)) * ((3 + 8 / 2) ^ 2) + 3
D. 6- ((2 + 3) * (3 + 8 / 2)) ^ 2 + 3
分析:这是前中后缀表达式问题。
中缀表达式就是实际算式。
把每一个符号往前移。
1,先把+往前找第一个空,得出(2+3)。
2,把-往前移,得出6-,拼接起来,就是6-(2+3)。
3,把/往前移,得出8/2。
4,把+往前移,得出(3+8/2)。
5,把*往前移,把两个前面算好的表达式拼接,得出((6- (2 + 3)) * (3 + 8 / 2))。
6,把^往前移,得出((6- (2 + 3)) * (3 + 8 / 2)) ^ 2。
此时已经得出结论,选择A。
9. 数101010(2进制)和166(8进制)的和为( D)。
A. 10110000(base-2)
B. 236(base-8)
C. 158(base-10)
D. A0(base-16)
分析:基础进制转换问题问题!!!
101010和166相加得到160(10进制)。
A不对。
B算出结果158,排除。
C看一眼,排除。
只剩D了。
10.假设有一组字符{a,b,c,d,e,f},对应的频率分别为5%、9%、12%、13%、16%、45%。请 问以下哪个选项是字符a,b,c,d,e,f分别对应的一组哈夫曼编码?(A )
A. 1111, 1110, 101, 100, 110, 0
B. 1010, 1001, 1000, 011, 010, 00
C. 000, 001, 010, 011, 10, 11
D. 1010, 1011, 110, 111, 00, 01
分析:哈夫曼树作图题。
把每一个频率最小的相加成为新值,再一个一个网上组成树,只剩一个时停下。
算了一下,过程:
规则:一个值到根的长度就是它的编码的长度。
f对应的45%到根的距离是1。
所以长度为1,只有A的0符合标准。
选A。
11.给定一棵二叉树,其前序遍历结果为:ABDECFG,中序遍历结果为:DEBACFG。请问这棵 树的正确后序遍历结果是什么?(A)
A. EDBGFCA
B. EDBGCFA
C. DEBGFCA
D. DBEGFCA
分析:前中后序遍历问题。
前序:根左右
中序:左根右
后序:左右根
根据规则,得出树的根是A,BDE为右侧,CFG为左侧。
- 左子树的中序遍历为:
D E B - 对应的前序遍历为:
B D E - 前序的第一个字母
B是左子树的根节点。 - 在中序
D E B中,B的左边是D E,右边为空。说明B只有左子树,没有右子树。 - 继续看
B的左子树(中序D E,前序D E):根节点是D。在中序D E中,D的右边是E,说明D只有右子树E。
- 右子树的中序遍历为:
C F G - 对应的前序遍历为:
C F G - 前序的第一个字母
C是右子树的根节点。 - 在中序
C F G中,C的左边为空,右边是F G。说明C只有右子树,没有左子树。 - 继续看
C的右子树(中序F G,前序F G):根节点是F。在中序F G中,F的右边是G,说明F只有右子树G。
然后继续推导。
所以:
A
/ \
B C
/ \
D F
/ \
E G
后序遍历的顺序是:左子树 -> 右子树 -> 根节点。
- 左子树
B-D-E的后序:先左后右再根,即E -> D -> B - 右子树
C-F-G的后序:先左后右再根,即G -> F -> C - 最后访问根节点
A
将它们拼接起来,最终的后序遍历结果为:E D B G F C A。
OK,选择A。
12.考虑一个有向无环图,该图包含4条有向边:(1,2), (1,3), (2,4)和(3,4)。以下哪 选项是这个有向无环图的一个有效的拓扑排序?( B)
A. 4, 2, 3, 1
B. 1, 2, 3, 4
C. 1, 2, 4, 3
D. 2, 1, 3, 4
分析:拓扑排序题。
会规则就简单。
每次取入度为0的点,重复。
先1,然后取2或3,最后4。
所以只能是:
1,2,3,4
或 1,3,2,4
只有B符合。
13.在计算机中,以下哪个选项描述的数据存储容量最小?(B)
A.字节(byte) B.比特(bit) C.字(word) D.千字节(kilobyte)
分析:这题拉完了。
常识,没办法解释。
知道bit最小即可。
14.一个班级有10个男生和12个女生。如果要选出一个3人的小组,并且小组中必须至少包 含1个女生,那么有多少种可能的组合?(A)
A.1420
B.1770
C.1540
D.2200
分析:组合题。
用逆向思维。
第一步:计算从全班任意选出3人的总组合数
班级总人数 = 10(男生) + 12(女生) = 22人。
从22人中任选3人的组合数为:
C(22,3)=22×21×203×2×1=1540C(22,3)=3×2×122×21×20=1540 种。
第二步:计算不满足条件的组合数(就是全是男生的组合)
题目要求“至少包含1个女生”,那么它的反面就是“一个女生都没有(全是男生)”。
从10个男生中任选3人的组合数为:
C(10,3)=10×9×83×2×1=120C(10,3)=3×2×110×9×8=120 种。
第三步:相减得出最终结果
至少包含1个女生的组合数 = 总组合数 - 全是男生的组合数
1540−120=14201540−120=1420 种。
因此,可能的组合共有1420种。
15.以下哪个不是操作系统?(D)
A.Linux B.Windows C.Android D.HTML
分析:这题要是不会,真没办法了。
HTML是编程语言。选D。
二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填√,错误填×;除特 殊说明外,判断题1.5分,选择题3分,共计40分)
(1)
#include <iostream> #include <cmath> using namespace std; double f(double a,double b, double c) { double s = (a + b +c) / 2; return sqrt(s * (s-a) * (s-b) * (s-c)); } int main() { cout.flags(ios::fixed); cout.precision(4); int a, b, c; cin >> a >> b >> c; cout << f(a, b, c) <<endl; return 0; }分析:看一眼代码,发现其核心行为是输入三个数,并且使用海伦公式来得出解。
16.(2分)当输入为“2 2 2”时,输出为“1.7321”。(T)当然对,算一下得出结论。
17.(2分)将第7行中的“(s-b) * (s-c)”改为“(s-c) * (s-b)”不会影响 程序运行的结果。(T)乘法交换律
18.(2分)程序总是输出四位小数。(T)这就要看基本功了,前面那个对cout的操作就是设置4位输出
19.当输入为“3 4 5”时,输出为(A)。
A. “6.0000” B. “12.0000” C. “24.0000” D. “30.0000”
一道计算题,这里就不写怎么算得了。
20.当输入为“5 12 13”时,输出为(B)。 A. “24.0000” B. “30.0000” C. “60.0000” D. “120.0000”
一道计算题。。。
(2)
#include <iostream> #include <vector> #include <algorithm> using namespace std; int f(string x, stringy) { int m = x.size(); int n = y.size(); vector<vector<int>>v(m+1, vector<int>(n+1, 0)); for (int i = 1; i <=m; i++) { for (int j = 1; j <=n; j++) { if (x[i-1] == y[j-1]){ v[i][j] = v[i-1][j-1]+ 1; } else { v[i][j] = max(v[i-1][j],v[i][j-1]); } } } return v[m][n]; } bool g(string x, string y) { if (x.size() != y.size()) { return false; } return f(x + x, y) == y.size();; } int main() { string x, y; cin >> x >> y; cout << g(x, y) << endl; return 0; }分析:这段代码的核心行为是判断y是否为x的最长公共子串。(根据图中数据得出结论)
分析出g的功能是判断x与y是否等长,不等则判断y是否为x的最长公共子串。
f函数则是通过dp,实现最长公共子序列算法,计算字符串x和y的最长公共子序列的长度。
接下来看题目:
21.f 函数的返回值小于等于min(n,m)。(T )
f函数计算的是字符串x和y的最长公共子序列的长度。
子序列是从原字符串中删除一些字符(也可以不删除)后得到的序列,因此它的长度不可能超过原字符串中较短的那个的长度。所以,他的长度必然小于等于min(m, n)。该说法正确。
22.f 函数的返回值等于两个输入字符串的最长公共子串的长度。(F )f返回的是子序列,不是字串。
23.当输入两个完全相同的字符串时,g函数的返回值总是true。( T)当x和y完全相同时,y一定是x+x的子串,因此它们的最长公共子序列长度等于y的长度,f(x+x, y) == y.size()成立,返回true。
24.将第19行中的“v[m][n]”替换为“v[n][m]”,那么该程序( D)。
A. 行为不变 B. 只会改变输出 C. 一定非正常退出 D. 可能非正常退出
有可能侥幸没超出边界,因此不一定100% RE。
25.当输入为“csp-j p-jcs”时,输出为(B )。
A. “0” B. “1” C. “T” D.“F”
不可能打印一个字符串,排除后两个。"p-jcs"确实是"csp-jcsp-j"的子串,选B。
26.当输入为“csppsc spsccp”时,输出为(D )。
A. “T” B. “F” C.0 D.1
"spsccp"是"csppscssppsc"的子串。选D。
(3)
#include <iostream> #include <cmath> using namespace std; int solve1(int n) { return n * n; } int solve2(int n) { int sum = 0; for (int i = 1; i <=sqrt(n); i++) { if (n % i == 0) { if (n/i == i) { sum += i*i; } else { sum += i*i + (n/i)*(n/i); } } } return sum; } int main() { int n; cin >> n; cout << solve2(solve1(n))<< " " << solve1(solve2(n)) << endl; return 0; }分析:solve1返回其平方,solve2通过一个函数确定其所有因子的平方和。
27.如果输入的n为正整数,solve2函数的作用是计算n所有的因子的平方和。(T)
阅读代码发现其逻辑确实是这样。
28.第13-14行的作用是避免n的平方根因子i(或n/i)进入第16行而被计算两次。(T)
是的,这样可以避免else,用来防止多计算。
29.如果输入的n为质数,solve2(n)的返回值为n*n+1。(T)
当输入的n为质数时:质数的正因子只有两个:1和n本身。
- 当
i = 1时,n%1==0成立。- 因为
n是质数(n>1),所以n/1!=1。 - 进入
else分支:sum += 1*1 + (n/1)*(n/1),即sum = 1 + n*n。
- 因为
- 当
i从2遍历到sqrt(n)时,因为n是质数,没有其他因子,所以n % i != 0,不会执行任何加操作。
故答案选择T。
30.如果输入的n为质数p的平方,那么solve2(n)的返回值为(D)
题目已知输入的n为质数p的平方,即n = p^2。
我们来找出n = p^2的所有正因子:
因为p是质数,所以p^2的正因子只有三个:1、p和p^2。
循环条件为i <= sqrt(n),即i <= sqrt(p^2),也就是i <= p。
然后通过模拟循环得出结论,选择D。
31.当输入为正整数时,第一项减去第二项的差值一定(D)。
A.大于0 B.大于等于0且不一定大于0 C.小于0 D.小于等于0且不一定小于0
前两个分析发现不会是正数。
第一项等于n^2的所有正因子的平方和。因此,第二项等于n的所有正因子的平方和 的平方。
因为n是正整数,它至少有一个因子1,所以因子平方和S >= 1^2 = 1。对于任何大于等于 1 的实数S,都有S^2 >= S。
这说明第二项 >= 第一项。
差值 = 第一项 - 第二项。因为 第一项 <= 第二项,所以 差值 <= 0。
因此选D。
32.当输入为“5”时,输出为(C)。
A. “651 625” B. “650 729” C. “651 676” D. “652 625”
直接计算得出结论。
三、完善程序(单选题,每小题 3 分,共计30分)
( 1)(寻找被移除的元素)问题:原有长度为n+1、公差为1的等差升序数列;将数列输入 到程序的数组时移除了一个元素,导致长度为n的升序数组可能不再连续,除非被移除的是第 一个或最后一个元素。需要在数组不连续时,找出被移除的元素。 试补全程序。
#include <iostream> #include <vector> using namespace std; int find_missing(vector<int>& nums) { int left = 0, right = nums.size()- 1; while (left < right) { int mid = left + (right- left) / 2; if (nums[mid] == mid + ①) { ②; } else { ③; } } return ④; } int main() { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; i++) cin >> nums[i]; int missing_number = find_missing(nums); if (missing_number == ⑤) { cout << "Sequence is consecutive" << endl; } else { cout << "Missing number is " << missing_number << endl; } return 0; }分析:经典二分题。
33.①处应填(A )
A. 1 B. nums[0] C. right D.left
mid是中点,nums[mid]就是nums的mid位置,这两个比较。
如果缺失了一个数字,那么在缺失数字之后的所有元素,其值都会比它的索引大1。所以要加1。
34.②处应填(A)
A. left = mid + 1 B. right = mid-1 C. right = mid D. left = mid
扩大二分边界,直接选择A。
35.③处应填(C)
A. left = mid + 1 B. right = mid-1 C. right = mid D. left = mid
缩小二分边界,直接选择C。
36.④处应填(A)
A. left + nums[0] B. right + nums[0] C. mid + nums[0] D. right + 1
left是边界答案,因此选择A,B理论上也可以,但是有可能出现误差。
37.⑤处应填(B)
A. nums[0]+n B. nums[0]+n-1 C. nums[0]+n+1 D. nums[n-1]
- A. nums[0]+n:这是数组连续时,下一个应该出现的数字。
- B. nums[0]+n-1:这是数组连续时,最后一个元素的值,也就是
nums[n-1]。 - C. nums[0]+n+1:这是数组连续时,下下一个应该出现的数字。
- D. nums[n-1]:这是数组连续时,最后一个元素的值。
故此选择B。
(2)(编辑距离)给定两个字符串,每次操作可以选择删除(Delete)、插入(Insert)、替换(Replace) 一个字符,求将第一个字符串转换为第二个字符串所需要的最少操作次数。 试补全动态规划算法。
#include <iostream> #include <string> #include <vector> using namespace std; int min(int x, inty, int z) { return min(min(x, y),z); } int edit_dist_dp(string str1, string str2) { int m = str1.length(); int n = str2.length(); vector<vector<int>>dp(m + 1, vector<int>(n + 1)); for (int i = 0; i <=m; i++) { for (int j = 0; j <=n; j++) { if (i == 0) dp[i][j] =①; else if (j == 0) dp[i][j] =②; else if (③) dp[i][j] =④; else dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j],⑤); } } return dp[m][n]; } int main() { string str1, str2; cin >> str1 >> str2; cout << "Minimum numberof operations: "<< edit_dist_dp(str1,str2) << endl; return 0; }分析:
min被修改为判断3个数的最小值,而edit_dist_dp是dp核心。
根据题意分析,dp[i][j]为把字符串str1的前i个字符,变成字符串str2的前j个字符,所需要的最少操作次数。
38.①处应填(A)
A. j B. i C. m D. n
我们的代码执行到i==0才会出现这个情况。
代表我们要把str1的前0个字符串(空串)变为str2的前j个字符串所需要的操作次数。
因为空,所以要加上j次。
39.②处应填(B)
A. j B. i C. m D. n
j==0的特殊情况。
代表我们要把str1的前i个字符串变为str2的前0个字符串所需要的操作次数。
减少i次,因此是i。
40.③处应填(A)
A. str1[i-1] == str2[j-1]
B. str1[i] == str2[j]
C. str1[i-1] != str2[j-1]
D. str1[i] != str2[j]
这次要求我们找特殊情况。而且它没有给出后面的修改。
我们看看后面和前面,全是修改,有没有可能,当前因为相同不用修改?
所以我们选则A,相同的情况。
41.④处应填(B)
A. dp[i-1][j-1] + 1 B. dp[i-1][j-1] C. dp[i-1][j] D. dp[i][j-1]
前面我们得知这是相同情况。
代表我们要把str1的前i个字符串变为str2的前j个字符串所需要的操作次数。
我们不需要修改增加,A是错的。
dp[i-1][j-1]代表我们要把str1的前i-1个字符串变为str2的前j-1个字符串所需要的操作次数。
这就是直接不修改了。选择B。
42.⑤处应填(C)
A. dp[i][j] + 1 B. dp[i-1][j-1] + 1 C. dp[i-1][j-1] D. dp[i][j]
最后就是i和j都正常的一般情况了。
前面dp[i-1][j],dp[i][j-1]。
不就是求删除(Delete)、插入(Insert)、替换(Replace)的最小值吗?
所以我们选择C,凑齐3种情况。
完成。