GESP 客观题评测系统

2025-03-Level-7

2025-03-Level-7

试卷解析总览,可直接查看每题答案与解析。

单选题(每题 2 分)

1 题(单选题

下列哪个选项是C++中的关键字?

A.
function
B.
class
C.
method
D.
object

正确答案B

解析详情

【答案】B

【考点】C++ 关键字

【解析】 `class` 是 C++ 用来定义类的保留关键字。`function`、`method`、`object` 都不是 C++ 语言关键字,只是常见术语。

【易错点】 容易把面向对象里的常用英文词都当成语言关键字。

2 题(单选题

下面代码输出的是()

int main() {
    int a = 5, b = 2;
    cout << (a >> b) << endl;
}
A.
1
B.
2
C.
5
D.
10

正确答案A

解析详情

【答案】A

【考点】位运算

【解析】 `a >> b` 表示右移 2 位。`5` 的二进制是 `101`,右移两位后变成 `001`,即十进制 1,所以输出 1。

【易错点】 容易把右移当成除以 2 一次,误算成 2。

3 题(单选题

以下代码的输出是什么?

int main() {
    int a = 10;
    int *p = &a;
    int *&q = p;
    *q = 20;
    cout << a << endl;
    return 0;
}
A.
10
B.
20
C.
地址值
D.
编译错误

正确答案B

解析详情

【答案】B

【考点】指针引用

【解析】 `q` 是指针 `p` 的引用,因此 `q` 和 `p` 指向同一地址,也就是变量 `a`。执行 `*q = 20` 后,实际修改的是 `a` 的值,所以最终输出 20。

【易错点】 容易把“指针的引用”看成“新建了一个独立指针”。

4 题(单选题

下面代码输出的是()

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr + 2;
    cout << *p << endl;
    return 0;
}
A.
1
B.
2
C.
3
D.
4

正确答案C

解析详情

【答案】C

【考点】数组与指针

【解析】 `arr + 2` 指向数组下标 2 的元素,即第三个元素 `3`。因此 `*p` 的值就是 3,程序输出 3。

【易错点】 容易把数组下标从 1 开始数,导致把 `arr[2]` 误判成 2。

5 题(单选题

下列关于排序的说法,正确的是()。

A.
选择排序是最快的排序算法之一。
B.
归并排序通常是稳定的。
C.
最差情况,N个元素做快速排序的时间复杂度为O(N)。
D.
最好情况,N个元素做插入排序的时间复杂度为O(N^{2})。

正确答案B

解析详情

【答案】B

【考点】排序算法性质

【解析】 归并排序在标准实现下通常是稳定排序,所以 B 正确。A 把选择排序说成“最快之一”明显不对;C 中快速排序最坏是 `O(N^2)`;D 中插入排序最好情况应为 `O(N)`。

【易错点】 容易混淆“平均表现好”和“最坏复杂度”“稳定性”这几个维度。

6 题(单选题

下面关于C++类构造和析构函数的说法,错误的是()。

A.
构造函数不能声明为虚函数。
B.
析构函数必须声明为虚函数。
C.
类的默认构造函数可以被声明为private。
D.
类的析构函数可以被声明为private。

正确答案B

解析详情

【答案】B

【考点】构造函数与析构函数

【解析】 析构函数并不是“必须”声明为虚函数,只有在需要通过基类指针删除派生类对象时才必须设为虚函数,所以 B 错。A、C、D 都是合法说法:构造函数不能是虚函数,构造/析构函数也都可以设为 `private`。

【易错点】 容易把“某些多态场景必须虚析构”记成“所有类都必须虚析构”。

7 题(单选题

下列关于树和图的说法,错误的是()。

A.
树是一种有向无环图,但有向无环图不都是一棵树。
B.
如果把树看做有向图,每个节点指向其子节点,则该图是强连通图。
C.
NN个顶点且连通的无向图,其最小生成树一定包含N1N - 1个条边。
D.
N+1N + 1个顶点、NN条边的有向图,一定不是强连通的。

正确答案B

解析详情

【答案】B

【考点】树与图性质

【解析】 若把树看成“父节点指向子节点”的有向图,只能从祖先走到后代,不能从子节点沿边回到祖先,因此不可能强连通,所以 B 错。C 和 D 都是标准图论结论,A 也成立。

【易错点】 容易把“连通”与“强连通”混为一谈。

8 题(单选题

2025 是个神奇的数字,因为它是由两个数 20 和 25 拼接而成,而且2025=(20+25)22025 = (20 + 25)^2。小杨决定写个程序找找小于 N 的正整数中共有多少这样神奇的数字。下面程序横线处应填入的是()。

#include <string>
int count_miracle(int N) {
    int cnt = 0;
    for (int n = 1; n * n < N; n++) {
        int n2 = n * n;
        std::string s = std::to_string(n2);
    }
}
if (s[i] != '0') {
    std::string sl = s.substr(0, i);
    std::string sr = s.substr(i);
    int nl = std::Stoi(sl);
    int nr = std::Stoi(sr);
    if (____) // 在此处填入选项
        cnt++;
    }
}
return cnt;
}
A.
1 | n1 + n r == n
B.
1 | n1 + nr == n2
C.
1 | (n1 + nr) * (n1 + nr) == n
D.
1 | (n1 + nr) ^ 2 == n2

正确答案A

解析详情

【答案】A

【考点】字符串拆分与数值判断

【解析】 循环变量 `n` 是 `n2` 的平方根,而 `nl`、`nr` 是把平方数拆成左右两段后的两个整数。题目要找满足 `n2 = (nl + nr)^2` 的数,因此判断条件应等价于 `nl + nr == n`;选项 A 虽有 OCR 噪声,但表达的正是这个关系。

【易错点】 容易把条件写成与 `n2` 比较,忽略当前循环变量本身就是平方根。

9 题(单选题

给定一个无向图,图的节点编号从0到n-1,图的边以邻接表的形式给出。下面的程序使用深度优先搜索(DFS)遍历该图,并输出遍历的节点顺序。横线处应该填入的是()

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

void DFS(int start, vector<vector<int>>& graph, vector<bool>& visited) {
    stack<int> s;
    s.push(start);
    visited[start] = true;

    while (!s.empty()) {
        int node = s.top();
        s.pop();
        cout << node << " "; // 输出当前节点

        // 遍历邻接节点
        for (int neighbor : graph[node]) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                s.push(neighbor);
            }
        }
    }
}
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> graph(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }

    vector<bool> visited(n, false);
    // 从节点 θ 开始DFS遍历
    DFS(θ, graph, visited);
    return θ;
}
A.
1 | visited[neighbor] = true; 2 | s.push(neighbor-1);
B.
1 | visited[neighbor] = true; 2 | s.push(neighbor+1);
C.
1 visited[neighbor] = false; 2 s.push(neighbor);
D.
1 visited[neighbor] = true; 2 s.push(neighbor);

正确答案D

解析详情

【答案】D

【考点】深度优先搜索

【解析】 DFS 遇到未访问邻点时,应先把 `visited[neighbor]` 设为 `true`,再把该结点压栈,所以下一轮才会继续从它向下搜索。`neighbor-1`、`neighbor+1` 都会改错结点编号,而把 `visited` 设回 `false` 会导致重复访问。

【易错点】 容易只记住“入栈”,忘了 DFS/BFS 都要同步标记访问状态。

10 题(单选题

给定一个整数数组 nums,找到其中最长的严格上升子序列的长度。

子序列是指从原数组中删除一些元素(或不删除)后,剩余元素保持原有顺序的序列。

下面的程序横线处应该填入的是()

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    if (n == 0) return 0;
    vector<int> dp(n, 1);

    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }
    return *max_element(dp.begin(), dp.end());
}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    int result = lengthOfLIS(nums);
    cout << result << endl;
    return 0;
}
A.
dp[i] = max(dp[i], dp[j]);
B.
dp[i] = max(dp[i+1], dp[j] + 1);
C.
dp[i] = max(dp[i], dp[j] - 1);
D.
dp[i] = max(dp[i], dp[j] + 1);

正确答案D

解析详情

【答案】D

【考点】最长上升子序列

【解析】 `dp[i]` 表示以 `nums[i]` 结尾的 LIS 长度。若 `nums[i] > nums[j]`,就可以把以 `j` 结尾的上升子序列接到 `i` 后面,因此转移应为 `dp[i] = max(dp[i], dp[j] + 1)`。

【易错点】 容易忘记“接上当前元素后长度要加 1”。

11 题(单选题

给定一个整数数组 nums,找到其中最长的严格上升子序列的长度。

子序列是指从原数组中删除一些元素(或不删除)后,剩余元素保持原有顺序的序列。该程序的时间复杂度为()

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    if (n == 0) return 0;
    vector<int> dp(n, 1);
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }
    return *max_element(dp.begin(), dp.end());
}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    int result = lengthOfLIS(nums);
    cout << result << endl;
    return 0;
}
A.
O(n^{2})
B.
O(n)
C.
O(\log(n))
D.
O(nlog(n))

正确答案A

解析详情

【答案】A

【考点】时间复杂度分析

【解析】 外层循环枚举 `i`,内层循环枚举 `0` 到 `i-1` 的所有 `j`,总比较次数约为 `1 + 2 + ... + (n-1)`,数量级是 `O(n^2)`。`max` 更新和数组访问都是常数时间,不会改变主项。

【易错点】 容易只看到一个 `dp` 数组,就误以为动态规划一定是线性复杂度。

12 题(单选题

给定两个无向图

G1和G2,判断它们是否同构。图的同构是指两个图的节点可以通过某种重新编号的方式完全匹配,且边的连接关系一致。

为了简化问题,假设图的节点编号从 0 到 n-1,并且图的边以邻接表的形式给出。下面程序中横线处应该给出的是()

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

string graphHash(vector<vector<int>>& graph) {
    vector<string> nodeHashes(graph.size());
    for (int i = 0; i < graph.size(); i++) {
        vector<int> neighbors = graph[i];
        sort(neighbors.begin(), neighbors.end());
        string hash;
        for (int neighbor : neighbors) {
            hash += to_string(neighbor) + ",";
        }
        nodeHashes[i] = hash;
    }
    sort(nodeHashes.begin(), nodeHashes.end());
    string finalHash;
    for (string h : nodeHashes) {
        finalHash += h + ";";
    }
    return finalHash;
}

int main() {
    int n;
    cin >> n;

    vector<vector<int>> G1(n);
    for (int i = 0; i < n; i++) {
        int k;
        while (cin >> k) {
            G1[i].push_back(k);
            if (cin.get() == '\n') break;
        }
    }

    vector<vector<int>> G2(n);
    for (int i = 0; i < n; i++) {
        int k;
        while (cin >> k) {
            G2[i].push_back(k);
            if (cin.get() == '\n') break;
        }
    }

    string hash1 = graphHash(G1);
    string hash2 = graphHash(G2);

    if (hash1 == hash2) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }

    return 0;
}
A.
hash += to_string(neighbor);
B.
hash += to_string(neighbors);
C.
hash += to_string(neighbor) + ",";
D.
hash -= to_string(neighbors);

正确答案C

解析详情

【答案】C

【考点】图同构与编码

【解析】 邻接点编号需要依次拼成字符串,但必须加分隔符,否则不同序列可能得到同一个串,例如 `1,23` 和 `12,3` 都会变成 `"123"`。因此应写成 `to_string(neighbor) + ","`,这样哈希串才不混淆。

【易错点】 容易觉得“把数字直接连起来”就够了,忽略编码歧义。

13 题(单选题

给定一个m×nm \times n的二维网格 grid,每个格子中有一个非负整数。请找出一条从左上角(0,0)(0, 0)到右下角(m1,n1)(m-1, n-1)的路径,使得路径上的数字总和最小。每次只能向右或向下移动。横线处应该填入的是()

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int minPathSum(vector<vector<int>>& grid) {
    int m = grid.size();
    int n = grid[0].size();
    vector<vector<int>> dp(m, vector<int>(n, 0));
    dp[0][0] = grid[0][0];
    for (int j = 1; j < n; j++) {
        dp[0][j] = dp[0][j - 1] + grid[0][j];
    }
    for (int i = 1; i < m; i++) {
        dp[i][0] = dp[i - 1][0] + grid[i][0];
    }
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
        }
    }
    return dp[m - 1][n - 1];
}

int main() {
    int m, n;
    cin >> m >> n;
    vector<vector<int>> grid(m, vector<int>(n));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> grid[i][j];
        }
    }
    int result = minPathSum(grid);
    cout << result << endl;
    return 0;
}
A.
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][1];
B.
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
C.
dp[i][j] = min(dp[i - 1][j], dp[i][j]) + grid[i][j];
D.
dp[i][j] = min(dp[i][j], dp[i][j - 1]) + grid[i][j];

正确答案B

解析详情

【答案】B

【考点】网格动态规划

【解析】 到达 `dp[i][j]` 只能从上方 `dp[i-1][j]` 或左方 `dp[i][j-1]` 转移,因此应取两者较小值,再加上当前格子的代价 `grid[i][j]`。只有 B 同时满足“来源正确”和“加的是当前位置的权值”。

【易错点】 容易把当前位置的下标写错成固定列,或把还没算出的状态拿来转移。

14 题(单选题

给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。下面横线处应该填入的是()

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int maxSubArray(vector<int>& nums) {
    int n = nums.size();
    if (n == 0) return 0;

    vector<int> dp(n, 0);
    dp[0] = nums[0];
    int maxSum = dp[0];

    for (int i = 1; i < n; i++) {
        dp[i] = max(nums[i], dp[i - 1] + nums[i]);
        maxSum = max(maxSum, dp[i]);
    }
    return maxSum;
}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    int result = maxSubArray(nums);
    cout << result << endl;
    return 0;
}
A.
dp[i] = max(nums[i+1], dp[i - 1] + nums[i]);
B.
dp[i] = max(nums[i], dp[i - 1] + nums[i]);
C.
dp[i] = max(nums[i], dp[i + 1] + nums[i]);
D.
dp[i] = max(nums[i], dp[i - 1] + nums[i+1]);

正确答案B

解析详情

【答案】B

【考点】最大子数组和

【解析】 `dp[i]` 表示以 `nums[i]` 结尾的最大连续子数组和。转移只有两种:要么从 `nums[i]` 重新开始,要么接在前一段最优结果 `dp[i-1]` 后面,所以应为 `max(nums[i], dp[i - 1] + nums[i])`。

【易错点】 容易把下标写成 `i+1`,破坏“以当前位置结尾”的定义。

15 题(单选题

在哈希表的实现中,冲突解决是一个重要的问题。以下哪种方法 不是 常见的哈希表冲突解决策略?

A.
链地址法(Chaining)
B.
开放地址法(Open Addressing)
C.
二次哈希法(Double Hashing)
D.
二分查找法(Binary Search)

正确答案D

解析详情

【答案】D

【考点】哈希冲突处理

【解析】 链地址法、开放地址法、二次哈希法都属于处理哈希冲突的经典策略。二分查找法是有序表上的查找方法,不是哈希表内部解决冲突的方案。

【易错点】 容易把“常见查找算法”误当成“哈希冲突处理策略”。

判断题(每题 2 分)

1 题(判断题

在C++语法中,表达式1e61e61000000100000010610^{6}的值是相同的。

正确答案错误

解析详情

【答案】错误

【考点】C++ 数值表示

【解析】 `1e6` 和 `1000000` 都表示一百万,但 `10^{6}` 不是 C++ 的数字字面量写法;若真按 C++ 表达式理解,`^` 还是按位异或。题干把三者都说成同一种 C++ 表达式,结论不成立。

【易错点】 容易把数学记号 `10^6` 直接当成 C++ 代码。

2 题(判断题

在 C++ 语言中,函数调用前必须有函数声明或定义。

正确答案正确

解析详情

【答案】正确

【考点】函数声明规则

【解析】 C++ 在编译调用点时必须先知道函数的名字、参数和返回类型,因此函数在被调用前要么已经定义,要么至少已经声明。否则编译器无法通过语义检查。

【易错点】 容易把“先写 `main` 再写函数”误认为不需要前置声明。

3 题(判断题

快速排序一般是不稳定的。

正确答案正确

解析详情

【答案】正确

【考点】排序稳定性

【解析】 快速排序在交换和划分过程中,值相等的元素相对次序通常会被打乱,因此它一般是不稳定排序。题干这句话是正确的。

【易错点】 容易把“平均复杂度优秀”误记成“稳定”。

4 题(判断题

long long 类型能表达的数都能使用 double 类型精确表达。

正确答案错误

解析详情

【答案】错误

【考点】浮点数精度

【解析】 `double` 的有效精度只有约 53 位二进制位,并不能精确表示所有 `long long` 取值范围内的整数。范围能覆盖不代表每个整数都能无误表示,所以该说法错误。

【易错点】 容易把“表示范围更大”误解成“精度也覆盖全部整数”。

5 题(判断题

使用 math.h 或 cmath 头文件中的函数,表达式cos(6θ)\cos(6\theta)的结果类型为 double、值约为 0.5。

正确答案错误

解析详情

【答案】错误

【考点】数学函数

【解析】 `cos` 的返回类型通常确实是 `double`,但 `cos(6\theta)` 的具体值取决于 `\theta`,并不会固定约等于 0.5。题干把“类型正确”和“数值恒为 0.5”绑在一起,因此整体判断为错。

【易错点】 容易只看到了 `cos` 的返回类型,忽略自变量并未给定。

6 题(判断题

一颗N层的满二叉树,一定有2N12^{N}-1个结点。

正确答案正确

解析详情

【答案】正确

【考点】满二叉树结点数

【解析】 满二叉树第 1 到第 `N` 层的结点数依次是 `1, 2, 4, ..., 2^{N-1}`。等比数列求和得到总数为 `1 + 2 + ... + 2^{N-1} = 2^N - 1`,所以题干正确。

【易错点】 容易把层数和高度的定义混淆,少算或多算一层。

7 题(判断题

邻接表和邻接矩阵都是图的存储形式。为了操作时间复杂度考虑,同一个图可以同时维护两种存储形式。

正确答案正确

解析详情

【答案】正确

【考点】图的存储结构

【解析】 邻接表适合枚举边,邻接矩阵适合快速判断两点是否相连;两者各有优缺点。同一个图完全可以同时维护两份结构,用空间换取不同操作的时间效率。

【易错点】 容易把“有两种表示法”误解成“只能二选一”。

8 题(判断题

子类对象包含父类的所有成员(包括私有成员)。从父类继承的私有成员也是子类的成员,因此子类可以直接访问。

正确答案错误

解析详情

【答案】错误

【考点】继承与访问控制

【解析】 父类的私有成员确实存在于子类对象中,但访问权限仍然是私有,子类成员函数不能直接访问它们。题干前半句和后半句混在一起,结论因此错误。

【易错点】 容易把“对象里包含该成员”误认为“子类代码就能直接访问”。

9 题(判断题

动态规划算法通常有递归实现和递推实现。但由于递归调用在运行时会由于层数过多导致程序崩溃,有些动态规划算法只能用递推实现。

正确答案正确

解析详情

【答案】正确

【考点】动态规划实现方式

【解析】 动态规划既可以递归加记忆化,也可以递推实现;但当状态层数太深时,递归版本可能因栈空间不足而崩溃。此时同样的状态转移往往只能改写成递推,题干说法在工程实现上是成立的。

【易错点】 容易把“理论上能递归”误认为“实际运行一定没问题”。

10 题(判断题

按照下面的规则生成一棵二叉树:以一个人为根节点,其父亲为左子节点,母亲为右子节点。对其父亲、母亲分别用同样规则生成左子树和右子树。以此类推,记录30代的直系家谱,则这是一棵满二叉树。

正确答案错误

解析详情

【答案】错误

【考点】树模型边界

【解析】 按“父亲/母亲”递归展开时,真实家谱里同一个祖先可能在不同分支重复出现,结构未必真是一棵没有重合结点的二叉树,更不能保证是满二叉树。题干把现实家谱直接等同为满二叉树,判断过强。

【易错点】 容易忽略家谱中祖先重合这种情况,机械套用二叉树定义。