GESP 客观题评测系统

2025-06-Level-6

2025-06-Level-6

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

单选题(每题 2 分)

1 题(单选题

下列哪一项不是面向对象编程的基本特征?

A.
继承
B.
封装
C.
多态
D.
链接

正确答案D

解析详情

【答案】D

【考点】面向对象编程特征

【解析】 面向对象编程(OOP)的三大基本特征是封装、继承和多态。链接(Linking)是程序编译链接过程中的一个步骤,不属于面向对象编程的核心语法特征。

【易错点】 容易将编程语言的三个基本特征与编译过程中的术语混淆。

2 题(单选题

为了让 Dog 类的构造函数能正确地调用其父类 Animal 的构造方法,横线线处应填入()。

class Animal {
public:
    std::string name;
    Animal(std::string str) : name(str) {
        std::cout << "Animal created\n";
    }
    virtual void speak() {
        cout << "Animal speaks" << endl;
    }
};

class Dog : public Animal {
    std::string breed;
public:
    Dog(std::string name, std::string b) : _____, breed(b) {
        std::cout << "Dog created\n";
    }
    void speak() override {
        cout << "Dog barks" << endl;
    }
};

int main() {
    Animal* p = new Dog("Rex", "Labrador");
    p->speak();
    delete p;
    return 0;
}
A.
Animal(name)
B.
super(name)
C.
Animal::Animal(name)
D.
Animal()

正确答案A

解析详情

【答案】A

【考点】C++ 派生类构造函数初始化列表

【解析】 在 C++ 中,子类构造函数在初始化列表中调用父类构造函数的语法是直接写“父类名(参数)”。由于父类 Animal 定义了带参数的构造函数且没有默认构造函数,必须在 Dog 的初始化列表中显式调用 Animal(name)。

【易错点】 误选 B(Java/Python 的 super 语法)或 C(错误的域解析符用法)。

3 题(单选题

代码同上一题,代码执行结果是()。

A.
输出 Animal speaks
B.
输出 Dog barks
C.
编译错误
D.
程序崩溃

正确答案B

解析详情

【答案】B

【考点】C++ 虚函数与多态

【解析】 父类 Animal 中的 speak 函数被声明为 virtual,且子类 Dog 使用 override 关键字对其进行了覆盖。虽然指针 p 是 Animal* 类型,但它实际指向的是 Dog 实例,在运行时会根据动态绑定调用子类的 speak(),输出“Dog barks”。

【易错点】 如果函数不是 virtual,则会根据指针类型调用父类方法输出 Animal speaks。

4 题(单选题

以下关于栈和队列的代码,执行后输出是()。

stack<int> s;
queue<int> q;

for (int i = 1; i <= 3; ++i) {
    s.push(i);
    q.push(i);
}
cout << s.top() << " " << q.front() << endl;
A.
13
B.
31
C.
33
D.
11

正确答案B

解析详情

【答案】B

【考点】栈与队列的基本性质

【解析】 循环依次推入 1、2、3。栈(stack)是后进先出(LIFO),最后进入的 3 在栈顶,故 s.top() 为 3;队列(queue)是先进先出(FIFO),最先进入的 1 在队首,故 q.front() 为 1。输出结果为 3 1。

【易错点】 记反栈和队列的存取规则。

5 题(单选题

在一个循环队列中,front 是指向队头的指针,rear 指向队尾的指针,队列最大容量为 maxSize。判断队列已满的条件是()。

A.
rear == front
B.
(rear + 1) % maxSize == front
C.
(rear - 1 + maxSize) % maxSize == front
D.
(rear - 1) == front

正确答案B

解析详情

【答案】B

【考点】循环队列判满条件

【解析】 循环队列为了区分队满和队空,通常会空出一个存储单元。当 rear 的下一个位置(取模后)等于 front 时,即判定为队列已满,公式为 (rear + 1) % maxSize == front。

【易错点】 容易与队空的判定条件(rear == front)混淆。

6 题(单选题

()只有最底层的节点未被填满,且最底层节点尽量靠左填充。

A.
完美二叉树
B.
完全二叉树
C.
完满二叉树
D.
平衡二叉树

正确答案B

解析详情

【答案】B

【考点】二叉树分类定义

【解析】 完全二叉树(Complete Binary Tree)的定义规定:深度为 k 的树,1 到 k-1 层都是满的,第 k 层如果不满,其节点也必须连续集中在左侧。这完全符合题干中“最底层节点尽量靠左填充”的描述。

【易错点】 误选 A 完美二叉树(即满二叉树,所有层都必须全满)。

7 题(单选题

在使用数组表示完全二叉树时,如果一个节点的索引为i(从0开始计数),那么其左子节点的索引通常是()。

A.
(i1)/2(i-1)/2
B.
i+1
C.
i*2
D.
2*i+1

正确答案D

解析详情

【答案】D

【考点】完全二叉树的数组存储结构

【解析】 当数组索引从 0 开始时,根节点在索引 0,其左右孩子分别在 1 和 2。通过归纳可知,节点 i 的左孩子索引为 2*i + 1,右孩子索引为 2*i + 2,父节点索引为 (i-1)/2。

【易错点】 若索引从 1 开始,左孩子则是 2*i,需注意起始索引值。

8 题(单选题

已知一棵二叉树的前序遍历序列为 GDAFEMHZ,中序遍历序列为 ADFGEHMZ,则其后序遍历序列为()。

A.
ADFGEHMZ
B.
ADFGHMEZ
C.
AFDGEMZH
D.
AFDHZMEG

正确答案D

解析详情

【答案】D

【考点】二叉树遍历还原

【解析】 1. 前序首字符 G 是根。中序中 G 分开左(ADF)和右(EHMZ)。 2. 左子树前序 DAF 中序 ADF,说明 D 是左根,其右子树前序 AF 中序 AF,A 是根其右是 F。 3. 右子树前序 EMHZ 中序 EHMZ,E 是右根,其右子树前序 MHZ 中序 HMZ,M 是根其左 H 右 Z。 4. 后序遍历:左->右->根,得到 AFDHZMEG。

【易错点】 中序遍历 (ADF)G(EHMZ) 中左子树 (ADF) 对应的根是前序中的 D 而非 A。

9 题(单选题

设有字符集 {a, b, c, d, e},其出现频率分别为 {5, 8, 12, 15, 20},得到的哈夫曼编码为()。

A.
a: 010
b: 011
c: 00
d: 10
e: 11
B.
a: 00
b: 10
c: 011
d: 100
e: 111
C.
a: 10
b: 01
c: 011
d: 100
e: 111
D.
a: 100
b: 01
c: 011
d: 100
e: 00

正确答案A

解析详情

【答案】A

【考点】哈夫曼树构造与编码

【解析】 1. 合并最小的 5(a) 和 8(b) 得到新节点 13。 2. 合并 12(c) 和 13 得到 25。 3. 合并 15(d) 和 20(e) 得到 35。 4. 合并 25 和 35 得到 60。 频率大的路径短,c 编码长度应为 2,d、e 为 2,a、b 为 3。选项 A 的编码长度配置(3,3,2,2,2)符合要求且满足前缀码规则。

【易错点】 哈夫曼编码不唯一,但必须满足频率越大的字符编码越短且无前缀冲突。

10 题(单选题

3 位格雷编码中,编码 101 之后的下一个编码不可能是()。

A.
100
B.
111
C.
110
D.
001

正确答案C

解析详情

【答案】C

【考点】格雷码(Gray Code)性质

【解析】 格雷码的核心性质是任意两个相邻的码字之间只有一位二进制数不同。比较 101 与各选项: A. 100(最后一位变,对) B. 111(中间位变,对) C. 110(中间和最后位都变,错) D. 001(第一位变,对)。故 110 不可能紧邻 101。

【易错点】 误以为格雷码必须按照特定的顺序排列,实际上题目只问“下一个不可能是”,只要有两位以上不同就不可能。

11 题(单选题

请将下列 C++ 实现的深度优先搜索(DFS)代码补充完整,横线处应填入()。

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};

void dfs(TreeNode* root, vector<int>& result) {
    if (root == nullptr) return;
    _______________________________
}
A.
result.push_back(root->val);
dfs(root->left, result);
dfs(root->right, result);
B.
result.push_back(root->left->val);
dfs(root->right, result);
dfs(root->left, result);
C.
result.push_back(root->left->val);
dfs(root->left, result);
dfs(root->right, result);
D.
result.push_back(root->right->val);
dfs(root->right, result);
dfs(root->left, result);

正确答案A

解析详情

【答案】A

【考点】二叉树深度优先遍历(先序遍历)

【解析】 DFS 在二叉树中通常表现为先访问根节点,再递归访问左孩子,最后递归访问右孩子(即先序遍历)。选项 A 首先将 root->val 存入结果,然后依次对左右子树调用 dfs,逻辑完整且语法正确。

【易错点】 B、C、D 选项在访问 root->left->val 或 root->right->val 前未判空,会导致非法内存访问。

12 题(单选题

给定一个二叉树,返回每一层中最大的节点值,结果以数组形式返回,横线处应填入()。

#include <vector>
#include <queue>
#include <algorithm>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};

vector<int> largestValues(TreeNode* root) {
    vector<int> result;
    if (!root) return result;

    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        int sz = q.size();

        int maxVal = INT_MIN;
        for (int i = 0; i < sz; ++i) {
            TreeNode* node;
            _______________________________
            maxVal = max(maxVal, node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        result.push_back(maxVal);
    }
    return result;
}
A.
node = q.end();
B.
node = q.front();
C.
q.pop();
node = q.front();
D.
node = q.front();
q.pop();

正确答案D

解析详情

【答案】D

【考点】二叉树层序遍历(BFS)与 STL 队列操作

【解析】 层序遍历的标准操作是:首先获取队头元素 `q.front()` 赋给当前节点指针,然后使用 `q.pop()` 将该元素移出队列。选项 D 顺序正确。如果先弹出再取 front,会导致跳过当前元素或程序崩溃。

【易错点】 注意 STL queue 的 pop() 函数不返回任何值,必须先 front() 再 pop()。

13 题(单选题

下面代码实现一个二叉排序树的插入函数(没有相同的数值),横线处应填入()。

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};

void insert(TreeNode* &root, int key) {
    if (!root) {
        root = new TreeNode(key);
        return;
    }
    _______________________________
}
A.
if (key < root->val)
    insert(root->left, key);
else if (key > root->val)
    insert(root->right, key);
B.
if (key < root->val)
    insert(root->right, key);
else if (key > root->val)
    insert(root->left, key);
C.
insert(root->left, key);
insert(root->right, key);
D.
insert(root->right, key);
insert(root->left, key);

正确答案A

解析详情

【答案】A

【考点】二叉排序树(BST)的性质与插入算法

【解析】 二叉排序树的插入规则是:新键值比根节点小时插入左子树,大时插入右子树。选项 A 严格遵循此逻辑进行递归调用,符合 BST 定义。

【易错点】 不要混淆左右子树的判断逻辑(如选项 B)。

14 题(单选题

以下关于动态规划算法特性的描述,正确的是()。

A.
子问题相互独立,不重叠
B.
问题包含重叠子问题和最优子结构
C.
只能从底至顶迭代求解
D.
必须使用递归实现,不能使用迭代

正确答案B

解析详情

【答案】B

【考点】动态规划(Dynamic Programming)核心特性

【解析】 动态规划有两个必要条件:1. 最优子结构(子问题的最优解能推导出原问题的最优解);2. 重叠子问题(相同的子问题在推导过程中被多次计算)。

【易错点】 选项 A 是分治法的特征;选项 C 错误,DP 也可以用带备忘录的自顶向下递归实现。

15 题(单选题

给定 n 个物品和一个最大承重为 W 的背包,每个物品有一个重量 wt[i] 和价值 val[i],每个物品只能选择放或不放。目标是选择若干个物品放入背包,使得总价值最大,且总重量不超过 W. 关于下面代码,说法正确的是()。

int knapsack1D(int W, vector<int>& wt, vector<int>& val, int n) {
    vector<int> dp(W+1, 0);
    for (int i = 0; i < n; ++i) {
        for (int w = W; w >= wt[i]; --w) {
            dp[w] = max(dp[w], dp[w - wt[i]] + val[i]);
        }
    }
    return dp[W];
}
A.
该算法不能处理背包容量为 θ 的情况
B.
外层循环 i 遍历背包容量,内层遍历物品
C.
从大到小遍历 w 是为了避免重复使用同一物品
D.
这段代码计算的是最小重量而非最大价值

正确答案C

解析详情

【答案】C

【考点】01背包问题的一维数组优化

【解析】 在一维 DP 实现 01 背包时,内层循环必须从大到小遍历。这是因为 `dp[w]` 需要参考“上一轮物品”的 `dp[w-wt[i]]`。如果从小到大遍历,计算当前 `dp[w]` 时引用的 `dp[w-wt[i]]` 可能已经是这一轮更新过的值,即重复使用了物品 i。

【易错点】 分不清 01 背包(逆序遍历)和完全背包(顺序遍历)的代码区别。

判断题(每题 2 分)

1 题(判断题

构造函数可以被声明为 virtual。

正确答案错误

解析详情

【答案】错误

【考点】C++ 虚函数与构造函数规则

【解析】 在 C++ 中,构造函数不能是虚函数。虚函数调用依赖于虚函数表指针(vptr),而 vptr 是在构造函数执行期间才被初始化的,逻辑上构造函数无法在 vptr 建立前进行动态派发。

【易错点】 注意析构函数通常需要声明为 virtual。

2 题(判断题

给定一组字符及其出现的频率,构造出的哈夫曼树是唯一的。

正确答案错误

解析详情

【答案】错误

【考点】哈夫曼树的构造性质

【解析】 哈夫曼树的构造过程中,如果存在频率相同的节点,合并顺序不唯一;此外,合并两个节点时左右子树的位置也可以互换。因此,同一组权值产生的树结构不唯一。

【易错点】 虽然树结构不唯一,但其带权路径长度(WPL)是唯一且最小的。

3 题(判断题

为了实现一个队列,使其出队操作(pop)的时间复杂度为O(1)O(1)并且避免数组删除首元素的O(n)O(n)问题,一种常见且有效的方法是使用环形数组,通过调整队首和队尾指针来实现。

正确答案正确

解析详情

【答案】正确

【考点】循环队列原理

【解析】 普通数组实现队列时,头部出队需移动所有后续元素,复杂度为 O(n)。环形数组通过指针 front 和 rear 的模运算移动,无需搬移元素,效率极高。

【易错点】 忽略循环队列判满时需要牺牲一个空间或增加标记位的细节。

4 题(判断题

对一棵二叉排序树进行中序遍历,可以得到一个递增的有序序列。

正确答案正确

解析详情

【答案】正确

【考点】二叉排序树(BST)遍历性质

【解析】 根据二叉排序树定义,左子树 < 根 < 右子树。中序遍历顺序恰好是“左-根-右”,因此输出序列必然递增有序。

【易错点】 只有中序遍历有此性质,前序和后序遍历不保证有序。

5 题(判断题

如果二叉搜索树在连续的插入和删除操作后,所有节点都偏向一侧,导致其退化为类似于链表的结构,这时其查找、插入、删除操作的时间复杂度会从理想情况下的O(logn)O(\log n)退化到O(nlogn)O(n \log n)

正确答案错误

解析详情

【答案】错误

【考点】二叉搜索树退化复杂度分析

【解析】 二叉搜索树退化为链表后,树的高度变为 n,所有查找、插入、删除操作都需要遍历树,时间复杂度退化为 O(n),而非 O(n log n)。

【易错点】 容易错误地认为 O(n log n) 是一种“更差”的情况,实际上 O(n) 在此处是退化的必然结果。

6 题(判断题

执行下列代码,my_dog.name 的最终值是 Charlie。

class Dog {
public:
    std::string name;
    Dog(std::string str) : name(str) {}
};

int main() {
    Dog my_dog("Buddy");
    my_dog.name = "Charlie";
    return 0;
}

正确答案正确

解析详情

【答案】正确

【考点】C++ 成员变量访问权限

【解析】 类 Dog 中的 name 被声明在 public 作用域下,因此可以直接通过对象实例 `my_dog.name` 进行赋值修改。初始值为 Buddy,最后被修改为 Charlie。

【易错点】 注意如果成员是 private 则不能直接修改。

7 题(判断题

下列 C++ 代码可以成功编译,并且子类 Child 的实例能通过其成员函数访问父类 Parent 的属性 value。

class Parent {
private:
    int value = 100;
};

class Child : public Parent {
public:
    int get_private_val() {
        return value; // 尝试访问父类的私有成员
    }
};

正确答案错误

解析详情

【答案】错误

【考点】C++ 继承与访问权限控制

【解析】 父类中的 `private` 成员对任何外部类(包括子类)都是不可见的。子类如果要访问父类成员,该成员必须声明为 `protected` 或 `public`。此代码会导致编译错误。

【易错点】 混淆 private(仅本类)和 protected(本类及派生类)的访问权限。

8 题(判断题

下列代码中的 tree 向量,表示的是一棵完全二叉树 (-1 代表空节点) 按照层序遍历的结果。

#include <vector>
std::vector<int> tree = {1, 2, 3, 4, -1, 6, 7};

正确答案错误

解析详情

【答案】错误

【考点】完全二叉树的层序遍历特征

【解析】 完全二叉树要求所有节点都连续地填充在左侧。在这个层序序列中,索引 4 为空 (-1),但其后的索引 5(6) 和 6(7) 却有节点,说明树中间存在空隙,违背了完全二叉树的定义。

【易错点】 二叉树可以这样存储,但它不是“完全”二叉树。

9 题(判断题

在树的深度优先搜索(DFS)中,使用栈作为辅助数据结构以实现“先进后出”的访问顺序。

正确答案正确

解析详情

【答案】正确

【考点】深度优先搜索(DFS)原理

【解析】 DFS 的递归实现本质上是利用了系统调用栈;如果使用非递归实现,也必须显式使用栈(Stack)结构。这种“后进先出”的特性保证了搜索能尽可能向深处延伸。

【易错点】 对比 BFS 使用的是队列(Queue)。

10 题(判断题

下面代码采用动态规划求解零钱兑换问题:给定 n 种硬币,第 i 种硬币的面值为 coins[i - 1],目标金额为 amt,每种硬币可以重复选取,求能够凑出目标金额的最少硬币数量;如果不能凑出目标金额,返回 -1。

int coinChangeDPComp(vector<int> &coins, int amt) {
    int n = coins.size();
    int MAX = amt + 1;

    vector<int> dp(amt + 1, MAX);
    dp[0] = 0;

    for (int i = 1; i <= n; i++) {
        for (int a = 1; a <= amt; a++) {
            if (coins[i - 1] > a)
                dp[a] = dp[a];
            else
                dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1);
        }
    }
    return dp[amt] != MAX ? dp[amt] : -1;
}

正确答案正确

解析详情

【答案】正确

【考点】动态规划之完全背包问题应用

【解析】 代码通过初始化 dp 数组为极大值,逐步更新凑成金额 a 所需的最少硬币。内层循环顺序遍历 a 保证了“每种硬币可以重复选取”的完全背包特性。逻辑无误。

【易错点】 注意初始化 `dp[0]=0` 是计算“最少数量”问题的关键。