1 单选题(每题 2 分,共 30 分)
第1题 在C++中,声明一个指向整型变量的指针的正确语法是( )。
A. int* ptr; B. *int ptr; C. int ptr*; D. ptr int;
解析:答案:A。指针变量定义语法: 数据类型 * 变量名; 所以int* ptr; 语法正确。故选A。
第2题 下面的函数接收一个3行4列的二维数组并输出其中元素,则横线上不能填写( )。
- void printArray(________) {
- for (int i = 0; i < 3; ++i)
- for (int j = 0; j < 4; ++j)
- std::cout << arr[i][j] << " ";
- }
A. int arr[3][4] B. int arr[][4] C. int (*arr)[4] D. int** arr
解析:答案:D。在C++中,“**”通常表示“指针的指针”。例如,在int** ptr; 中,ptr是一个指向“指针”的指针,而不是二维数组。A. 接收3行4列数组,符合要求;B. 接收二维数组行可省略、列必须给出,可以接收3行4列数组,符合要求;C. *arr接收行指针,每行4列,可以接收3行4列数组,符合要求。故选D。
第3题 在C++中,int arr[3][4]和int* arr = new int[12]均可模拟一个行列的二维数组。关于这两种方式,下面说法错误的是( )。
A. int arr[3][4]在栈上分配空间,适合数组较小的情况;
B. int* arr = new int[12] 在堆上分配空间,数组较大时也适用;
C. 这两种方式申请的内存空间都是连续的。
D. 这两种方式申请的内存都能自动释放。
解析:答案:D。在C++中,常规类型变量定义在栈上,内存自动管理,超出作用域自动释放,适合小型数组(通常几KB以内),访问速度快,但大小必须在编译时确定;动态分配内存定义在堆上,需要手动管理内存(new分配的必须使用delete[]释放),适合大型数组(可达GB级别),大小可以在运行时确定,访问速度稍慢于栈内存。A.、B.正确。C.静态和动态分配内存都连续的,正确。D. 静态申请的内存会自动释放,动态申请的内存要手动管理、手动释放,错误。故选D。
第4题 关于以下C++代码,说法正确的是( )。
- int main() {
- greet();
- return 0;
- }
- void greet() {
- cout << "Hello!" << endl;
- }
A. 正确编译并输出Hello! B. 编译错误:找不到函数greet()
C. 编译警告但可以运行 D. 链接错误
解析:答案:B。在C++中,函数引用必须在函数定义或声明函数原型之后,如函数引用前未进行函数原型声明,函数双定义在引用之后,编译时会引发“xxx未在此范围内声明”,其中xxx为函数名,B.正确。因不能完成编译A.、C.、D.错误。故选B。
第5题 在C++中,如果希望通过函数修改传入的结构体对象的内容,应该使用哪种参数传递方式?( )
A. 值传递或引用传递 B. 值传递或指针传递
C. 引用传递或指针传递 D. 仅指针传递
解析:答案:C。在C++中,函数用值传递,形式参数(形参)与实际参数(实参)独立,形参的改变不影响实参。函数用指针传递,形式参数(形参)获得的是实际参数(实参)的地址,形参与实参为同一地址,对形参的修改就是对实参修改。函数用引用传递,形式参数(形参)是实际参数(实参)的别名,两者为不同名称的同一变量,对形参的修改就是对实参修改。希望通过函数修改传入的结构体对象的内容用指针传递和引用传递均可。故选C。
第6题 以下哪个选项正确描述了C++中形参和实参的区别?( )
A. 形参是函数调用时传递给函数的具体值,实参是函数定义中声明的变量。
B. 形参是函数定义中声明的变量,实参是函数调用时传递给函数的具体值。
C. 形参和实参在函数调用时是完全相同的。
D. 形参只在函数内部可见,实参在函数外部可见。
解析:答案:B。在C++中,实参是函数调用时传递给函数的具体值,形参是函数定义中声明的变量,A.错误、B.正确、C.错误。在C++中,形参只在函数定义内部可见,是函数声明时括号内定义的变量,作用域仅限于该函数体内,函数调用时由实参初始化,函数返回后形参就被销毁;实参在函数调用处可见,是调用函数时传入的具体值或变量,作用域取决于它原本的定义位置,可以是常量、变量或表达式,函数调用后仍然存在(除非是临时对象或常量)D.错误。故选B。
第7题 运行如下代码会输出( )。
- int value = 100;
- void print1() {
- int value = 50;
- cout << value << " ";
- cout << ::value << " ";
- }
- void print2() {
- cout << value << " ";
- }
- print1();
- print2();
A. 100 100 100 B. 50 50 50 C. 50 100 100 D. 50 50 100
解析:答案:C。在C++中,在函数print1()中,第5行输出的是第4行定义的局部变量value,::value是批全局变量,即第1行定义的value,所以print1()输出“50 100 ”。 在函数print1()中,没有定义同名局部变量value,所以第10行输出的是全局变量value(全局变量所有函数可见),所以print2()输出“100 ”,合起来输出“50 100 100”。故选C。
第8题 小杨在整理一副扑克牌的所有红心扑克牌,使其从小到大排列。他的做法是:最开始抓到第1张扑克牌被认为已经排好序;然后抓第2张扑克牌,将其插入至有序部分的正确位置;不断循环步骤,每次将新抓到扑克牌插入至有序部分,直至抓完所有扑克牌,这样抓牌结束时就完成了扑克牌的排序。小杨这种整理扑克牌的方式与( )排序 的方式最接近。
A. 冒泡排序 B. 插入排序 C. 选择排序 D. 直接排序
解析:答案:B。冒泡排序是两两交换将最大(升序)的元素交换到最右侧,然后对剩下n-1个元素用同样方法将次大元素交换到n-1的最右侧,以此类推。插入排序将有序无序两部分,初始时选第1个元素为有序,将无序中的第1个元素插入到有序中正确位置,以此类推。选择排序从未排序中选出一个归小值交换到未排序的最左侧,以此类推。直接排序算法分为直接插入排序算法和直接选择排序算法两种。故选B。
第9题 以下哪种情况是使用插入排序的合适场景?
A. 数据量非常大,且乱序严重 B. 希望获得稳定排序,但不要求实时性
C. 数据几乎有序,只需少量调整 D. 想在交换次数最少的前提下排好大数组
解析:答案:C。A.数据量非常大,且乱序严重 用 归并排序,时间复杂度稳定为O(n log n),不受数据分布影响(快速排序在乱序时最坏可能退化到 O(n²))。B. 希望稳定排序,不要求实时性 用 归并排序,归并排序是稳定排序(相同元素相对位置不变),满足核心需求,在稳定排序算法中,归并排序是唯一达到 O(n log n) 复杂度的算法(冒泡/插入排序为 O(n²),效率过低)。C. 数据几乎有序,只需少量调整 用 插入排序,最佳时间复杂度 O(n):当数据接近有序时,插入排序仅需少量比较和移动,效率远超其他算法,算法简单且稳定,适合小规模调整。D. 交换次数最少排好大数组 → 若数组规模 n ≤ 10⁴ 用 选择排序(优先最小化交换);若数组规模 n > 10⁴ → 堆排序(牺牲少量交换次数换取时间效率)。故选C。
第10题 以下关于递推算法基本思想的描述,正确的是( )。
A. 递推算法通过将问题分解为相互独立的子问题来解决。
B. 递推算法从已知的基础情况出发,通过某种关系逐步推导出更大规模问题的解。
C. 递推算法通常用于穷举所有可能的解决方案。
D. 递推算法适用于在每一步做出局部最优选择以达到全局最优。
解析:答案:C。递推算法的基本思想是利用已知信息(初始条件)和问题中各元素之间的固定关系(递推关系),按照一定的规律或规则,从初始状态开始一步步推导出后续状态(或目标状态)的解。从最简单、最小规模的子问题(通常是问题的起点或边界)开始求解;利用规模较小的子问题的解(通常是已经计算并存储的结果),作为基础,按照明确的规则(递推公式、状态转移方程)去计算规模稍大的子问题的解。A.错误。B.正确。C.不穷举所有解,错误。D.递推不存在局部最优解问题,错误。故选B。
第11题 给定如下算法,其时间复杂度为( )。
- bool f(int arr[], int n, int target) {
- for (int i = 0; i < n; i++) {
- int sum = 0;
- for (int j = 0; j < n; j++) {
- if (i & (1 << j)) {
- sum += arr[j];
- }
- }
- if (sum == target) return true;
- }
- return false;
- }
A. O(n) B. O(n²) C. O(n³) D. O(2ⁿ)
解析:答案:B。原则上,几重循环就是O的n几次方,两重循环,时间复杂度为O(n²)。故选B。
第12题 下述斐波那契数列计算的时间复杂度是( )。
- int fibonacci(int n) {
- if (n == 0) return 0;
- if (n == 1) return 1;
- return fibonacci(n - 1) + fibonacci(n - 2);
- }
A. O(n) B. O(n²) C. O(n³) D. O(2ⁿ)
解析:答案:D。当参数为n时,时间复杂度为f(n)=f(n-1)+f(n-2),调用形成二叉树,树的高度h=n-1,共调用次数,即节点数(按满二叉树计)2ʰ⁻¹-1=2ⁿ⁻²-1个。时间复杂度为f(2ⁿ⁻²-1)=O(2ⁿ)。故选D。
第 13 题 关于下面 C++ 程序的描述,( )最准确。
- ifstream in("data.txt");
- string line;
- while (getline(in, line)) {
- cout << line << endl;
- }
A. 将从标准输入读取每行,并输出到屏幕
B. 程序无法运行,因为 getline 只能读取 cin
C. 将 data.txt 中的每一行读取并输出到屏幕
D. 程序将创建 data.txt 并写入默认文本
解析:答案:C。getline()的原型为istream& getline (istream& is, string& str);,is是一个流,例如cin,str是一个string类型的引用,读入的字符串将直接保存在str里面。第1行in为从文件data.txt读入的输入流,第3、4行为从in(文件data.txt)按行读入并显示(输出到屏幕),直到文件结束。A.错误,B.错误,D.错误。故选C。
第14题 在C++中,异常处理机制(try-catch块)的主要目的是 ( )。
A. 提高程序的运行速度。
B. 在程序发生运行时错误时,提供一种结构化的错误处理方式。
C. 确保程序在编译时没有错误。
D. 减少程序的内存占用。
解析:答案:B。异常处理机制(try-catch块)的主要目的是提供一种结构化、安全且非侵入式的方法来处理程序运行时的错误或异常情况。A.错误,C.错误,D.错误。故选B。
第 15 题 为了提高冒泡排序的效率,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返 回结果,则两条横线上分别应该填写( )。
- void bubbleSortWithFlag(vector &nums) {
- for (int i = nums.size() - 1; i > 0; i--) {
- bool flag;
- ________________ // 在此处填入代码
- for (int j = 0; j < i; j++) {
- if (nums[j] > nums[j + 1]) {
- swap(nums[j], nums[j + 1]);
- ___________________________ // 在此处填入代码
- }
- }
- if (!flag)
- break;
- }
- }
A. |
| B. |
|
C. |
| D. |
|
解析:答案:B。由于第12行中断条件为!flag,也就是flag=false,即第6-11行如没有交换flag=false。所以第9行有交换应该填flag=true,而第4行应该填flag=false,如没有交换不会执行第9行,flag保持flase。故选B。
2 判断题(每题 2 分,共 20 分)
第1题 下面C++代码正确声明了一个返回 int 类型、接受两个 int 参数的函数。( )
- int add(int, int);
解析:答案:正确。函数原型(函数声明)可以不写形参变量名,只需各形参的类型即可。故正确。
第2题 下面C++代码的输出是15。( )
- void foo(int x) {
- x += 5;
- }
- int main() {
- int a = 10;
- foo(a);
- cout << a << endl;
- }
解析:答案:错误。函数foo()为值传递,修改形参不影响实参,所以函数内x=15,但不影响实参a,所以调用函数foo(a)后a仍为10,输出结果为10。故错误。
第3题 下面c++代码在一个结构体中又定义了别的结构体。这种结构嵌套定义的方式语法不正确。( )
- #include <iostream>
- #include <vector>
- using namespace std;
- struct Library {
- struct Book {
- struct Author {
- string name;
- int birthYear;
- };
- string title;
- int year;
- Author author;
- };
- string name;
- vector<Book> books;
- };
解析:答案:错误。C++明确支持结构体类型的嵌套声明,允许在结构体内定义其他结构体类型,这种嵌套方式类似“俄罗斯套娃”的数据组织方式,能有效处理层级数据关系。代码中,最外层Library结构体包含图书馆名称和书籍集合,中间层Book结构体包含书名、出版年份和作者信息,最内层Author结构体存储作者姓名和出生年份,所以程序语法正确。故错误。
第4题 在C++中,相比于值传递,使用引用传递作的优点可以直接操作和修改原始变量,避免数据拷贝,提高效率。( )
解析:答案:正确。引用传递作的形参相当于实参的别名,对形参的操作,就是对实参的操作。在C++中,引用传递相比值传递确实具有显著优势,1.避免数据拷贝:引用传递不会创建对象副本,特别是对大型数据结构(如vector/string)能显著提升性能;2.减少内存开销:传递引用仅传递内存地址(通常4/8字节),而值传递需要复制整个对象。故正确。
第5题 下面这段代码不合法,因为每一行都必须显式初始化3个元素。( )
- int arr[2][3] = {{1, 2}, {3}};
解析:答案:错误。该代码是合法的,C++允许部分初始化多维数组,未显式初始化的元素会自动设为0,题目中代码等效于:int arr[2][3] ={{1,2,0}, {3,0,0}}。故错误。
第6题 以下程序中使用了递推方式计算阶乘(n!=1×2×⋯×n),计算结果正确。( )
- int factorial(int n) {
- int res = 1;
- for (int i = 0; i < n; ++i) {
- res *= i;
- }
- return res;
- }
解析:答案:错误。该代码第3行i应该从1到n,从0开始res永远为0(res=res*0=0, res=res*i=0*i=0),无法得到正确结果,要得到正确结果第3行应修改为for (int i = 1; i <= n; ++i) {。故错误。
第7题 无论初始数组是否有序,选择排序都执行O(n²)次比较。( )
解析:答案:正确。选择排序问题从未排序中选其中最小(或最大)的元素与区域第1个元素交换,与数据是否有序无关,时间复杂度为O(n²)。故正确。
第8题 以下C++代码,尝试对有 n 个整数的数组 arr 进行排序。这个代码实现了选择排序算法。( )
- for (int i = 0; i < n - 1; ++i) {
- int minIndex = i;
- for (int j = i + 1; j < n; ++j) {
- if (arr[j] < arr[minIndex])
- minIndex = j;
- }
- if (minIndex != i)
- swap(arr[i], arr[minIndex]);
- }
解析:答案:正确。外层循环:控制排序轮数,每次确定一个最小元素的位置i从0遍历到n-2(共需n-1轮);内层循环:在未排序部分(i+1到n-1)中查找最小元素,通过minIndex记录当前最小元素的下标(初始为i);元素交换:每轮结束后,将找到的最小元素与第i个位置交换,swap()函数实现元素位置互换。为是典型的选择排序,对数组arr进行升序排序。故正确。
第9题 如果一个异常在 try 块中抛出但没有任何 catch 匹配,它将在编译时报错。( )
解析:答案:错误。C++编译器不会因为try块中抛出的异常未被捕获而报错,语法上只要求try必须搭配至少一个catch块,但不强制catch必须匹配所有可能的异常类型。运行时,当抛出的异常未被任何catch块捕获时,程序会调用std::terminate()终止执行。故错误。
第10题 下面C++代码实现将Hello写入 data.txt。( )
- ofstream out("data.txt");
- out << "Hello";
- out.close();
解析:答案:正确。这段C++代码的功能是向文件写入数据:创建ofstream(写文件输出流)对象out并关联到"data.txt"文件,使用流插入运算符<<写入字符串"Hello",显式调用close()关闭文件流。若文件不存在会自动创建新文件,若文件已存在会覆盖原有内容(默认模式),最终文件内容为单行"Hello"字符串。故正确。
3 编程题(每题 25 分,共 50 分)
3.1 编程题1
- 试题名称:画布裁剪
- 时间限制:1.0 s
- 内存限制:512.0 MB
3.1.1 题目描述
小A在高为h宽为w的矩形画布上绘制了一幅画。由于画布边缘留白太多,小A想适当地裁剪画布,只保留画的主体。具体来说,画布可以视为h行w列的字符矩阵,其中的字符均为 ASCII 码位于33~126之间的可见字符,小A只保留画布中由第x₁行到第x₂行、第y₁列到第y₂列构成的子矩阵。 小A将画布交给了你,你能帮他完成画布的裁剪吗?
3.1.2 输入格式
第一行,两个正整数h,w,分别表示画布的行数与列数。
第二行,四个正整数x₁,x₂,y₁,y₂,表示保留的行列边界。
接下来h行,每行一个长度为w的字符串,表示画布内容。
3.1.3 输出格式
输出共x₂-x₁+1行、每行一个长度为y₂-y₁+1的字符串,表示裁剪后的画布。
3.1.4 样例
3.1.4.1 输入样例1
- 3 5
- 2 2 4
- .....
- .>_<.
- .....
3.1.4.2 输出样例1
- >_<
3.1.4.3 输入样例2
- 5 5
- 1 2 3 4
- AbCdE
- fGhIk
- LmNoP
- qRsTu
- VwXyZ
3.1.4.4 输出样例2
- Cd
- hI
3.1.5 数据范围
对于所有测试点,保证1≤h,w≤100,1≤x₁,x₂≤h,1≤y₁,y₂≤w。
3.1.6 编写程序思路
分析:本题可以用二维数组来做,先定义二维字符数组,按行、列输入n行、m列字符(画布),然后截取x1、y1到x2、y2范围内的元素输出。完整程序代码如下:
#include <iostream>
using namespace std;const int N = 105; //最多100,从1开始并适当留余量
char a[N][N];
int main() {int n, m, x1, x2, y1, y2;cin >> n >> m;cin >> x1 >> x2 >> y1 >> y2;for (int i = 1; i <= n; i++) //行从1开始for (int j = 1; j <= m; j++) //列也从1开始cin >> a[i][j]; //输入元素for (int i = x1; i <= x2; i++) { //行从x1到x2for (int j = y1; j <= y2; j++) //列从y1到y2cout << a[i][j]; //输出指定范围内元素cout << endl;}return 0;
}
3.2 编程题2
- 试题名称:排序
- 时间限制:1.0 s
- 内存限制:512.0 MB
3.2.1 题目描述
体育课上有n名同学排成一队,从前往后数第i位同学的身高为hᵢ,体重为wᵢ。目前排成的队伍看起来参差不齐,老师希望同学们能按照身高从高到低的顺序排队,如果身高相同则按照体重从重到轻排序。在调整队伍时,每次只能交换相邻两位同学的位置。老师想知道,最少需要多少次交换操作,才能将队伍调整成目标顺序。
3.2.2 输入格式
第一行,一个正整数n,表示队伍人数。
接下来n行,每行两个正整数hᵢ和wᵢ,分别表示第i位同学的身高和体重。
3.2.3 输出格式
输出一行,一个整数,表示最少需要的交换次数。
3.2.4 样例
3.2.4.1 输入样例1
- 5
- 1 60
- 3 70
- 2 80
- 4 55
- 4 50
3.2.4.2 输出样例1
- 8
3.2.4.3 输入样例2
- 5
- 4 0
- 4 0
- 2 0
- 3 0
- 1 0
3.2.4.4 输出样例2
- 1
3.2.5 数据范围
对于所有测试点,保证1≤n≤3000,hᵢ,wᵢ≤10⁹。
3.2.6 编写程序思路
分析:本题是成对元素的数组,用vector<pair<int,int>>可变数组是节省空间的,但GESP C++四级标准中未涉及可变数组。由于元素是成对的可考虑用结构体数组。定义两个成员a、b的结构体数组s[],成员a为身高、成员b为体重,读入数据,要求按身高从高到低的顺序排队,如果身高相同则按照体重从重到轻排序,交换判断条件s[i].a<s[j].a || (s[i].a==s[j].a && s[i].b<s[j].b)。完整程序代码如下:
#include <iostream>
using namespace std;const int N = 3005; //数据范围不超过3000
struct {int a;int b;
} s[N]; //定义两个成员的结构体数组int main() {int n, cnt = 0;cin >> n; //输入学生人数for (int i = 0; i < n; i ++)cin >> s[i].a >> s[i].b; //输入每人的身高、体重for (int i = 0; i < n; i ++)for (int j = i + 1; j < n; j ++)if (s[i].a < s[j].a || (s[i].a == s[j].a && s[i].b < s[j].b))cnt ++;cout << cnt << endl;return 0;
}