【单选题】(每题2分)
1、以下不属于计算机输出设备的有?( )
A、麦克风 B、音箱 C、打印机 D、显示器
正确答案:A
试题解析:麦克风是属于计算机的输入设备。
2、小明想了一个 1~100 之间的整数。你可以做多次猜测,每次猜测之后,如果你没有猜中,小明会告诉你,你猜的数比他想的数大还是小。你希望你在运气最坏的情况下花费最少的次数猜中,请问你运气最坏的情况下会猜( )次?(包括最后猜中的那次)
A、5 B、6 C、7 D、100
正确答案:C
试题解析:根据二分查找算法的思想,为了在运气最坏的情况下最少次数猜中,我们可以每次将搜索范围缩小一半。
对于N个数字,最多的猜测次数则是LogN。
本例中,因为2的6次方是64,而2的7次方128,所以最多需要7次可以猜中。
3、关于分治算法,下列说法错误的是?( )
A、分治算法的核心思想是分而治之,即把问题转化为多个规模更小的子问题求解。 B、分治算法可以不使用递归实现。
C、分治算法的时间复杂度是 O(logN),其中 N 表示问题的规模。 D、分治算法通常较容易在多核处理器上实现加速。
正确答案:C
试题解析:选项A是分治算法的核心思想,是正确的。
选项B,虽然分治算法经常用递归来实现,但也可以使用迭代或循环等方式实现。
选项C,分治算法的时间复杂度通常是 O(n log n) 或者 O(n^2),这取决于具体的问题和算法设计。只有在对半分割的情况下,时间复杂度才是 O(logN)。
选项D,因为分治算法可以将问题分解为多个子问题,并行处理这些子问题,可以在多核处理器上实现加速。
4、有关下面C++代码说法错误的是?( )
#include <iostream>
using namespace std;
int factA(int n) {
if (n <= 1)
return 1;
int ret = 1;
for (int i = 2; i <= n; ++i)
ret *= i;
return ret;
}
int factB(int n) {
return n == 1 ? 1 : n * factB(n - 1);
}
int main() {
int n;
cin >> n;
cout << factA(n)<<' '<< factB(n) << endl;
return 0;
}
A、factA()采用循环方式求n的阶乘,factB()采用递归方式求n的阶乘
B、程序执行时如果输入5,能实现求其阶乘,程序输出结果为120 120 C、任何递归程序都可以使用循环改写
D、程序执行时如果输入100,不能正确求出100的阶乘
正确答案:C
试题解析:理论上所有递归都可以用循环解决,但是大量的递归用循环实现代码复杂度较大,且易出错。
程序执行时如果输入100,结果超过int类型的表示范围,不能正确求出100的阶乘。
5、下面C++代码意在实现字符串反序的功能。关于这段代码,以下说法正确的是?( )
#include <iostream>
#include <cstring>
using namespace std;
void printSReverse(char *sIn, int len) {
if (len <= 1) {
cout << sIn[0];
} else {
cout << sIn[0];
printSReverse(sIn + 1, len - 1);
}
}
int main() {
char sIn[100] = "Hello";
printSReverse(sIn, strlen(sIn));
return 0;
}
A、这段代码可以正确实现字符串反序的功能,其输出为olleH
B、这段代码不能正确实现字符串反序的功能,其输出为Hello
C、这段代码不能正确实现字符串反序的功能,其输出为HHHHH
D、这段代码不能正确实现字符串反序的功能,其输出为ooooo
正确答案:B
试题解析:这段程序不能正确实现字符串反序,原因在于printSReverse()函数中的else部分程序是先打印sIn[0],再进行递归调用;
把cout<<sIn[0];这句移到printSReverse(sIn + 1, len - 1);则可以正确对字符串反序,这样递归函数返回的时候会依次打印反序的字符。
6、阅读下面C++实现的二分查找代码,下列说法中错误的是?( )
int binarySearch(int *arr, int l, int r, int x) {
if (r >= l) {
int mid = l + (r - l) / 2;
if (arr[mid] == x)
return mid;
else if (arr[mid] > x)
return binarySearch(arr, l, mid - 1, x);
else
return binarySearch(arr, mid + 1, r, x);
} else
return -1;
}
A、上面代码实现的二分查找,最少只需查找一次即可得到结果。
B、如果调用该函数在列表{2, 3, 4, 10, 12}中查找元素0,则它实际被调用3次。
C、如果调用该函数在列表{2, 3, 4, 10, 12}中查找元素3,则它实际被调用3次。
D、如果调用该函数在列表{2, 3, 4, 10, 12}中查找元素10,则它实际被调用3次。
正确答案:D
试题解析:选项D是错的。
因为binarySearch()中的l,r指的是数组的起始和终止下标,对于数组{2, 3, 4, 10, 12},查找元素10时:
第一次调用binarySearch时,mid=2,arr[mid]=4,比10小,继续第二次调用binarySearch(arr, 3, 4, 10),mid = l + (r - l) / 2=3,arr[mid]的值为10,已经找到元素10,返回mid;
所以实际上它被调用了2次。
7、使用如下代码片段定义四个字符串(假设头文件已正确定义),以下说法错误的是?( )
string str1 = "abc";
string str2 = str1;
char str3[] = "abc";
char *str4 = str3;
A、对于四个字符串,都可以使用std::cout输出其中的内容(例如,cout << str3;)。
B、str3只占用4字节内存,但str1却要占用更多内存。
C、由于str2由str1直接赋值得到,因此二者指向同一块内存,即修改str1的内容后str2的内容也会随之改变。
D、由于str4由str3直接赋值得到,因此二者指向同一块内存,即修改str3的内容后str4的内容也会随之改变。
正确答案:C
试题解析:C++ 中的 string 是一个标准库类型,它被设计用来方便地处理字符串。它实际上是一种动态数组,可以自动管理其内存。
使用string str2 = str1;将为str2重新分配内存,和str1存储内存不同,所以修改str1的内容不会影响str2。
8、有关下面C++代码正确的是?( )
#include <iostream>
using namespace std;
void f1() {
cout <<"f1()" << endl;
}
void f1(int x) {
cout << "f1("<< x <<")" << endl;
}
int main() {
f1();
f1(0);
f1('0');
return 0;
}
A、该程序不能正常运行,因为f1函数被重复定义。 B、该程序可以正常运行,输出结果共3行,依次为 f1()、f1()、f1()。
C、该程序可以正常运行,输出结果共3行,依次为 f1()、f1(0)、f1(0)。
D、该程序可以正常运行,输出结果共3行,依次为 f1()、f1(0)、f1(48)。
正确答案:D
试题解析:函数f1被重载了两次:一次没有参数,另一次带一个int类型的参数。
调用f1();打印的是“f1()”;
调用f1(0);打印的则是“f1(0)”;
调用f1('0');打印的是'0'的ASCII码,“f1(48)”;
9、关于C++程序的异常处理,以下选项中描述错误的是?( )
A、编程语言中的异常和错误是不同的概念。 B、异常一旦发生,程序便一定不能继续执行。
C、通过try、catch等保留字提供异常处理功能。 D、程序使用throw在任何地方抛出各种异常。
正确答案:B
试题解析:选项B的说法错误;
C++ 中的异常处理机制允许程序捕获和处理异常,从而使程序能够在异常发生后继续执行或采取适当的措施。
10、下面代码执行后的输出是?( )
#include <iostream>
using namespace std;
int fibonacci(int N) {
cout << N << ",";
if (N == 1 || N == 2) {
return 1;
} else {
return fibonacci(N - 1) + fibonacci(N - 2);
}
}
int main() {
cout << fibonacci(5) << endl;
return 0;
}
A、5,4,3,2,1,2,1,5 B、5,4,3,2,1,2,3,2,1,5 C、5,4,4,3,2,1,3,2,1,5 D、5,4,3,2,1,3,2,1,5
正确答案:B
试题解析:调用 fibonacci(5):
进入 fibonacci(5),输出 5,;
调用 fibonacci(4):
进入 fibonacci(4),输出 4,;
调用 fibonacci(3):
进入 fibonacci(3),输出 3,;
调用 fibonacci(2):
进入 fibonacci(2),输出 2,;
返回 1;
调用 fibonacci(1):
进入 fibonacci(1),输出 1,;
返回 1;
返回 fibonacci(2) + fibonacci(1),即 2;
调用 fibonacci(2):
进入 fibonacci(2),输出 2,;
返回 1;
返回 fibonacci(3 - 1) + fibonacci(3 - 2),即 3;
调用 fibonacci(3):
进入 fibonacci(3),输出 3,;
调用 fibonacci(2):
进入 fibonacci(2),输出 2,;
返回 1;
调用 fibonacci(1):
进入 fibonacci(1),输出 1,;
返回 1;
返回 fibonacci(2) + fibonacci(1),即 2;
返回 fibonacci(4 - 1) + fibonacci(4 - 2),即 5;
最终输出为:5,4,3,2,1,2,3,2,1,5。
11、下列代码中,函数f的作用是?( )
int f(int a, int b) {
return b == 0 ? a : f(b, a % b);
}
A、求a和b的最大公共质因子 B、求a和b的最小公共质因子 C、求a和b的最大公约数 D、求a和b的最小公倍数
正确答案:C
试题解析:f函数接受两个参数a和b,如果b为0,则返回a。
如果b不为0,那么函数会递归地调用自身,将b和a对b的余数(即a % b)作为新的参数,直到b为0为止。
因此,这个函数最终会返回a和b的最大公约数。所以正确答案是C、求a和b的最大公约数。
12、下面C++代码用于排序,下列说法中错误的是?( )
void sortA(int *arr, int n) {
for (int i = 0; i < n; ++i)
for (int j = 0; j < n - i - 1; ++j)
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
void sortB(int *arr, int start, int end) {
if (start >= end)
return;
int middle = (start + end) / 2;
sortB(arr, start, middle);
sortB(arr, middle + 1, end);
int leftSize = middle - start + 1;
int rightSize = end - middle;
int *left = new int[leftSize];
int *right = new int[rightSize];
for (int i = 0; i < leftSize; i++)
left[i] = arr[start + i];
for (int j = 0; j < rightSize; j++)
right[j] = arr[middle + 1 + j];
int i = 0;
int j = 0;
int k = start;
while (i < leftSize && j < rightSize) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
while (i < leftSize) {
arr[k] = left[i];
i++;
k++;
}
while (j < rightSize) {
arr[k] = right[j];
j++;
k++;
}
delete[] left;
delete[] right;
}
A、两种排序算法的时间复杂度不同。 B、两种排序算法的空间复杂度一致。
C、sortA的时间复杂度在最好和最坏情况下都是。
D、sortB的平均时间复杂度、最好情况的时间复杂度都是 O(logN),最坏情况的时间复杂度是。
正确答案:D
试题解析:sortA 函数使用的是冒泡排序(Bubble Sort)算法,sortB 函数使用的是归并排序(Merge Sort)算法。
冒泡排序的时间复杂度与输入数据的初始状态有关,最好情况下的时间复杂度是 O(N),即输入数据已经是有序的情况下,只需要遍历一次数据即可完成排序。最坏情况下的时间复杂度是 O(N^2)。
归并排序的时间复杂度与输入数据的规模有关,平均时间复杂度、最好情况的时间复杂度和最坏情况的时间复杂度都是 O(NlogN)。
13、上一题中的sortB函数,明显体现出的算法思想和编程方法包括?( )
A、递归 B、分治 C、A、B都正确 D、A、B都不正确
正确答案:C
试题解析:在给定的sortB函数中,我们可以看到明显的递归和分治思想和编程方法的体现。
所以选择答案C。
14、下列哪个算法并没有体现分治思想?( )
A、二分查找。 B、埃氏筛法。 C、归并排序。 D、快速排序。
正确答案:B
试题解析:没有体现分治思想的是埃氏筛法(二分查找体现了分治思想;C和D都是排序,也体现了分治思想)。
因为埃氏筛法是一种简单的筛选法,它的基本思想是从2开始,逐步筛选出所有合数,直到只剩下素数为止。它并没有将问题分解为更小的子问题,然后再合并这些子问题的解以得到原问题的解。与此相反,它通过逐步筛选来解决问题。
15、下列关于链表说法,正确的是?( )
A、不能用数组来实现链表。 B、在链表头部插入元素的时间复杂度是 O(1)。
C、循环链表使得任意一个结点都可以很方便地访问其前驱与后继。
D、从双向链表的任意一个节点出发,并不一定能够访问到所有其他节点。
正确答案:B
试题解析:当在链表头部插入一个新节点时,只需要移动一次指针,这个操作只需要常数时间就能完成,因此时间复杂度为 O(1)。
事实上,我们可以使用数组来模拟实现链表的基本功能。
循环链表并不会自动提供访问每个节点的前驱和后继的便利性。
双向链表的特点是每个节点既有指向下一个节点的指针,也有指向前一个节点的指针,因此从任意一个节点出发,都可以遍历访问到所有其他节点。
【判断题】(每题2分)
16、计算机硬件主要包括运算器、控制器、存储器、输入设备和输出设备。( )
正确答案:正确
试题解析:该说法正确。计算机硬件主要包括运算器、控制器、存储器、输入设备和输出设备。
17、唯一分解定理指的是分解质因数只有唯一的一种算法。( )
正确答案:错误
试题解析:唯一分解定理指的是每个大于1的自然数都可以唯一地分解为质数的乘积,而不是指分解质因数只有唯一的一种算法。因此,该说法是不正确的。
18、埃氏筛法用于找出自然数N以内的所有质数,其时间复杂度为,因为判定一个数是否为质数的时间复杂度为 O(N)。( )
正确答案:错误
试题解析:埃氏筛法是一种用于求解素数的算法。它通过逐步筛掉合数来得到素数。
埃氏筛法的时间复杂度为O(nlog(log(n)))。
19、贪心法的每一步都采取局部最优策略,因此必然能实现全局最优。( )
正确答案:错误
试题解析:该说法不一定正确。
贪心算法通过每一步选择局部最优解来构建全局最优解,但并不能保证每一步都能得到最优解,因为可能存在某些情况,使得在当时看来的局部最优解却不能导致全局最优解。
20、在C++语言中,函数的参数也可以是另一个函数。( )
正确答案:正确
试题解析:在C++语言中,函数可以有另一个函数作为它的参数,这是一种函数指针作为函数参数的使用方式。
21、在C++语言中,内置的排序算法(algorithm库中的sort函数)只能对C++的基础类型(如 int、double 等)做排序,而不能对自定义类型做排序。( )
正确答案:错误
试题解析:该说法不正确。
在C++语言中,内置的排序算法(如algorithm库中的sort函数)可以应用于自定义类型,而不仅限于基础类型。这是通过重载比较运算符(<、>、<=、>=)来实现的。
22、在任何场景下,链表都是比数组更优秀的数据结构。( )
正确答案:错误
试题解析:该说法不正确。
链表和数组都是常见的数据结构,它们都有自己的优缺点,适用于不同的场景。
23、在C++语言中,可以使用delete来释放指针指向的内存空间。( )
正确答案:正确
试题解析:该说法是正确的。
在 C++ 中,使用 new 运算符可以动态地分配内存空间,返回的是指向新分配的内存空间的指针。
为了避免内存泄漏,需要在不需要使用这些内存空间时手动释放它们。可以使用 delete 运算符来释放一个指针指向的动态分配的内存空间。
24、选择排序和快速排序都是不稳定的。( )
正确答案:正确
试题解析:该说法正确。选择排序和快速排序都是不稳定的排序算法。
25、二分查找法可以应用于有序序列(如升序排序的整数数组),也可以应用于无序序列(如乱序的整数数组)。
正确答案:错误
试题解析:该说法不正确。
二分查找法只能应用于有序序列,如果使用二分查找法在无序序列中搜索元素,可能会得到错误的结果。
【编程题】
26、小杨的锻炼问题
【问题描述】
小杨的班级里共有 N 名同学,每位同学都有各自的锻炼习惯。具体来说,第 i 位同学每隔 ai 天就会进行一次锻炼(也就是说,每次锻炼会在上一次锻炼的 ai 天后进行)。
某一天,班上的 N 名同学恰好都来进行了锻炼。他们对此兴奋不已,想要计算出下一次所有同学都来锻炼,至少要过多少天。但他们不会计算,你能帮帮他们吗?
【输入描述】
第一行一个整数 N,表示同学的数量。
第二行 N 个用空格隔开的正整数,依次为 a0,a1,…,aN?1。
【输出描述】
输出一个整数,表示下一次所有同学都来锻炼,至少要过多少天。
【特别提醒】
在常规程序中,输入、输出时提供提示是好习惯。但在本场考试中,由于系统限定,请不要在输入、输出中附带任何提示信息。
【样例输入1】
3
1 2 3
【样例输出1】
6
【样例解释1】
第一位同学每天都锻炼;第二位同学每 2 天锻炼一次;第三位同学每 3 天锻炼一次。因此,6 天之后,三位同学都会进行锻炼。在此之前,第二位同学只会在第 2,4 天进行锻炼,第三位同学只会在第 3 天进行锻炼,他们都无法相遇。
【样例输入2】
4
2 4 8 16
【样例输出2】
16
【样例解释2】
第四位同学每 16 天锻炼一次,而第 16 天后也恰好是前三位同学锻炼的日子。
【样例输入3】
4
2 4 6 8
【样例输出3】
24
【数据规模】
对于 20 的测试点,保证 N = 2。
对于 50 的测试点,保证 N ≤ 4。
对于所有测试点,保证 2 ≤ N ≤ 10,1 ≤ ai ≤ 50。
【参考程序】
#include <iostream>
using namespace std;
unsigned long long gcd(unsigned long long a, unsigned long long b) {
if (a > b) swap(a, b);
while (a % b) {
unsigned long long t = a % b;
a = b;
b = t;
}
return b;
}
//此题要点找到最小公倍数
int main() {
int N;
cin >> N;
unsigned long long lcm;
cin >> lcm;
for (int i = 1; i < N; i++) {
unsigned long long t;
cin >> t;
unsigned long long g = gcd(lcm, t); // 找到两数的最大公约数
lcm = lcm * t / g; // 找到两数的最小公倍数
}
cout << lcm;
}
27、小杨的列队问题
【问题描述】
小杨的班级里共有 N 名同学,学号从 0 至 N - 1。
某节课上,老师要求同学们进行列队。具体来说,老师会依次点名 M 名同学,让他们加入队伍。每名新入队的同学需要先站到队伍末尾(刚开始队伍里一个人都没有,所以第一个入队的同学只需要站好即可),随后,整个队伍中的所有同学需要按身高从低到高重新排序(身高相同的同学之间的顺序任意)。
排队很容易,但重新排序难倒了同学们。稍加讨论后,他们发现可以通过交换位置的方法来实现排序。具体来说,他们可以让队伍中的两名同学交换位置,这样整个队伍的顺序就会发生变化,多经过这样的几次交换后,队伍的顺序就可以排好。
例如:队伍中有 4 名同学,学号依次为 10、17、3、25,我们可以令 3 号同学和 10 号同学交换位置,则交换后的队伍顺序变为 3、17、10、25,这就是一次交换位置。
聪明的小杨想要知道:在老师每次点名一位新同学加入队伍后,在原有队伍的基础上,同学们最少要进行几次交换位置,才能完成老师按身高排序的要求。
【输入描述】
第一行一个整数 N,表示同学的数量。
第二行 N 个用空格隔开的正整数,依次表示学号为 0、1、…、N - 1 的同学的身高(不超过 2,147,483,647)。
第三行一个整数 M,表示老师点名的数量。
接下来 M 行,依次描述 M 次点名:每行一个整数 x(0≤x<N),表示要求学号为 x 的同学加入队伍。保证该名同学此前不在队伍中。
对于所有的测试点,保证 1≤M≤N≤2,000。对于 50 的测试点,保证所有同学的身高互不相同。
【输出描述】
输出 M 行,依次表示对于每次点名,同学们最少要进行几次交换位置,才能完成按身高排序的要求。
【特别提醒】
在常规程序中,输入、输出时提供提示是好习惯。但在本场考试中,由于系统限定,请不要在输入、输出中附带任何提示信息。
【样例输入1】
5
170 165 168 160 175
4
0
3
2
1
【样例输出1】
0
1
1
2
【样例解释1】
初始时队伍为空,身高为 170 的 0 号同学加入队伍,不需要任何交换位置。
接着,身高为 160 的 3 号同学加入队伍的末尾,此时两位同学需要进行依次交换位置,才能保证身高更矮的 3 号同学排在身高更高的 0 号同学前面。
接着,身高为 168 的 2 号同学加入队伍的末尾,此时队伍中的同学学号(身高)依次为 3(160)、0(170)、2(168),此时 2 号同学可以和 0 号同学进行一次交换位置,即可完成排序要求。
接着,身高为 165 的 1 号同学加入队伍的末尾,此时队伍中的同学学号(身高)依次为 3(160)、2(168)、0(170)、1(165),此时可以令 1 号同学和 2 号同学进行一次交换位置,使队伍变为 3(160)、1(165)、0(170)、2(168);随后再令 0 号同学和 2 号同学进行一次交换位置,使队伍变为 3(160)、1(165)、2(168)、0(170),即可完成排序要求。
【样例输入2】
4
20 20 20 10
4
0
1
2
3
【样例输出2】
0
0
0
1
【样例解释2】
前三位加入队伍的同学(0、1、2 号同学)身高都相同,不需要进行任何交换位置。最后加入队伍的 3 号同学身高最矮,需要和队头的 0 号同学交换位置,方可完成排序要求。
【参考程序】
#include <iostream>
#include <vector>
using namespace std;
int main() {
uint64_t N, M;
cin >> N; // 总共N个同学
uint64_t a[N];
for (uint64_t i = 0; i < N; i++) cin >> a[i]; // 输入所有身高
cin >> M; // 输入需要排队的人数
uint64_t b[M] = {0}; // 定义新的排队队列
uint64_t ct[M] = {0};
for (uint64_t i = 0; i < M; i++) { // 排队
uint64_t t;
cin >> t;
b[i] = a[t]; // 排入队尾
ct[i] = 0; // 交换位置的次数
for (uint64_t j = 0; j < i; j++) {
if (b[j] > b[i]) {
swap(b[j], b[i]);
ct[i]++;
}
}
}
for (uint64_t i = 0; i < M; i++) {
cout << ct[i] << endl;
}
}