夜雨聆风学习资料网

ARTICLE · 1196932

C++ GESP 2026年9月六级真题及解析

C++ GESP 2026年9月六级真题及解析

一、单项选择题(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)

  1. 下列关于 C++ 中继承和多态的描述中,错误的是( )。

A. 通过基类指针调用虚函数时,会根据对象实际类型决定调用版本。

B. 基类析构函数常声明为虚函数,以便通过基类指针正确释放派生类对象。

C. 派生类可以重写基类中的虚函数。

D. 构造函数可以声明为 virtual,以便在构造对象时实现动态绑定。

【解析】构造函数在对象构造时调用,此时对象的动态类型尚未确立,无法进行动态绑定,因此 C++ 明确规定构造函数不能声明为虚函数。

【答案】D

  1. 下列代码中,d1->work(); 和 d2->work(); 输出不同结果的主要原因是( )。
classDevice {
public:
virtualvoidwork(){
cout << "Device is working" << endl;
    }
virtual ~Device() {}
};
classPrinter :public Device {
public:
voidwork()override{
cout << "Printer is printing" << endl;
    }
};
classScanner :public Device {
public:
voidwork()override{
cout << "Scanner is scanning" << endl;
    }
};
intmain(){
    Device* d1 = new Printer();
    Device* d2 = new Scanner();
    d1->work();
    d2->work();
delete d1;
delete d2;
return0;
}

A. Printer 和 Scanner 使用了相同的构造函数。

B. work() 是虚函数,且 d1 和 d2 实际指向不同派生类对象,发生动态绑定。

C. d1 和 d2 是不同的指针变量。

D. 程序中使用了 delete 释放对象。

【解析】work() 被声明为 virtual,两个派生类分别重写。通过基类指针调用虚函数时,运行期根据指针实际指向的对象类型选择对应版本,这就是动态绑定(多态),因此 d1 调用 Printer::work、d2 调用 Scanner::work。指针是否为不同变量、构造函数、delete 都与输出内容无关。

【答案】B

  1. 下面代码在 main() 中有一行会导致编译错误,请找出来。
classStudent {
public:
    Student(string n, int s) : name(n), score(s) {}
stringgetName(){
return name;
    }
voidsetScore(int s){
        score = s;
    }
private:
string name;
int score;
};
intmain(){
Student stu("Tom", 85);
cout << stu.getName();   // ①
    stu.setScore(90);        // ②
    stu.score = 100;         // ③
cout << stu.getName();   // ④
return0;
}

A. 第 ① 行

B. 第 ② 行

C. 第 ③ 行

D. 第 ④ 行

【解析】score 是类的 private 成员,只能在类的成员函数内部访问。①④ 调用的是 public 成员函数,②调用的是 public 的 setScore,都合法;③在类外直接访问私有成员 stu.score,违反访问权限,编译报错。

【答案】C

  1. 某文本编辑器把用户输入的字符依次压入栈 S。用户依次输入 X, Y, Z, W 后,连续执行两次撤销操作。每次撤销都会弹出栈顶一个字符。此时栈从栈底到栈顶的内容是( )。

A. X Y

B. X Y Z

C. Y Z

D. X Z

【解析】依次压栈后,栈底到栈顶为 X、Y、Z、W(栈顶是 W)。第一次撤销弹出 W,第二次撤销弹出 Z,剩余从栈底到栈顶为 X Y。

【答案】A

  1. 假设循环队列数组长度为 N = 7,队空判断条件为 front == rear。入队和出队操作如下:
constint N = 7;
int q[N];
int front = 3, rear = 3;
voidenqueue(int x){
    q[rear] = x;
    rear = (rear + 1) % N;
}
voiddequeue(){
    front = (front + 1) % N;
}

依次执行:

enqueue(10);
enqueue(20);
enqueue(30);
dequeue();
enqueue(40);
dequeue();
enqueue(50);

最终 (front, rear) 的值是( )。

A. (5, 1)

B. (4, 0)

C. (5, 0)

D. (3, 1)

【解析】逐步跟踪指针(front 只受 dequeue 影响,rear 只受 enqueue 影响):

enqueue(10):q[3]=10,rear=4;

enqueue(20):q[4]=20,rear=5;

enqueue(30):q[5]=30,rear=6;

dequeue():front=4;

enqueue(40):q[6]=40,rear=(6+1)%7=0(发生回绕);

dequeue():front=5;

enqueue(50):q[0]=50,rear=(0+1)%7=1。

最终 (front, rear) = (5, 1)。

【答案】A

  1. 以下函数 check() 用于判断一棵二叉树是否为( )。
boolcheck(TreeNode* root){
if (!root) returntrue;
queue<TreeNode*> q;
    q.push(root);
bool hasNull = false;
while (!q.empty()) {
        TreeNode* cur = q.front();
        q.pop();
if (cur == nullptr) {
            hasNull = true;
        } else {
if (hasNull) returnfalse;
            q.push(cur->left);
            q.push(cur->right);
        }
    }
returntrue;
}

A. 满二叉树

B. 完全二叉树

C. 二叉搜索树

D. 平衡二叉树

【解析】层序遍历(BFS)中,空节点(nullptr)也一并入队,并用 hasNull 记录"已经遇到过空节点"。一旦在遇到空节点之后又出现非空节点,说明节点没有按层序连续排列,返回 false——这正是完全二叉树的定义(前 k 层满、最后一层节点靠左连续)。

【答案】B

  1. 以下代码实现了二叉树的哪种遍历方式?
voidtraverse(TreeNode* root){
if (root == nullptr) return;
cout << root->val << " ";
    traverse(root->left);
    traverse(root->right);
}

A. 前序遍历

B. 中序遍历

C. 后序遍历

D. 层序遍历

【解析】访问顺序是先输出根节点,再递归左子树,最后递归右子树,即"根 → 左 → 右",属于前序遍历(先序遍历)。中序是左根右,后序是左右根,层序需要队列。

【答案】A

  1. 已知一棵二叉树的先序遍历序列为:A B D E H C F G,中序遍历序列为:D B H E A F C G,则该二叉树的后序遍历序列是( )。

A. D H E B F G C A

B. D E H B F G C A

C. H D E B F C G A

D. D H E B G F C A

【解析】先序首元素 A 为根;中序中 A 左侧 D B H E 为左子树、右侧 F C G 为右子树。

左子树:先序 B D E H、中序 D B H E → 根 B,左孩子 D,右子树先序 E H、中序 H E → 根 E,H 为 E 的左孩子。

右子树:先序 C F G、中序 F C G → 根 C,左孩子 F,右孩子 G。

后序 = 左子树 + 右子树 + 根 = D H E B + F G C + A = D H E B F G C A。

【答案】A

  1. 有 6 个字符,它们出现的次数分别为:{3, 4, 7, 8, 12, 15},现在用哈夫曼编码为这些字符编码,最小加权路径长度 WPL 的值为( )。

A. 113

B. 119

C. 126

D. 31

【解析】每次取当前最小的两个权值合并:

3 + 4 = 7 → 集合 {7, 7, 8, 12, 15}

7 + 7 = 14 → {8, 12, 14, 15}

8 + 12 = 20 → {14, 15, 20}

14 + 15 = 29 → {20, 29}

20 + 29 = 49 → 合并完成

WPL 等于所有中间结点权值之和:7 + 14 + 20 + 29 + 49 = 119。

【答案】B

  1. 对 n 个不同符号进行哈夫曼编码。若生成的哈夫曼树共有 63 个结点,则 n 的值是( )。

A. 31

B. 32

C. 63

D. 64

【解析】哈夫曼树每个内部结点都有两个孩子。设叶子数为 n,则内部结点数为 n − 1,总结点数 = n + (n − 1) = 2n − 1。由 2n − 1 = 63 得 n = 32。

【答案】B

  1. 在格雷码中,相邻两个编码只能有一位不同。若当前编码为 110,则它的下一个编码不可能是( )。

A. 010

B. 111

C. 100

D. 001

【解析】逐个比较与 110 的差异位数:010 只差第 1 位;111 只差第 3 位;100 只差第 2 位;而 001 与 110 的第 1、2、3 位全都不同(差 3 位),不符合"只差一位"的要求。

【答案】D

  1. 给定一棵二叉树,采用广度优先搜索 BFS 返回其右视图,其中右视图中的每个节点都是该层最右侧的节点。横线处应填写( )。
vector<int> rightSideView(TreeNode* root){
vector<int> result;
if (!root) return result;
queue<TreeNode*> q;
    q.push(root);
while (!q.empty()) {
int sz = q.size();
for (int i = 0; i < sz; ++i) {
            TreeNode* node = q.front();
            q.pop();
__________________________
if(node->left) q.push(node->left)
;
if (node->right) q.push(node->right);
        }
    }
return result;
}

A. if (i == 0) result.push_back(node->val);

B. if (i == sz - 1) result.push_back(node->val);

C. result.push_back(q.front()->val);

D. if (node->right) result.push_back(node->right->val);

【解析】sz 记录的是当前层的节点个数,内层循环按顺序处理本层的每一个节点。右视图取每层最右边的节点,即本层第 sz 个(下标 sz − 1)节点,故在 i == sz - 1 时把 node->val 存入结果。A 取的是每层最左侧(左视图);C 取的是下一个待处理节点,且可能取到别的层;D 是下一层节点,位置也不对。

【答案】B

  1. 下面代码实现二叉搜索树的插入操作。假设树中不存在重复值,横线处应填写( )。
TreeNode* insertNode(TreeNode* root, int x){
if (root == nullptr) {
returnnew TreeNode(x);
    }
if (x < root->val) {
        __________________________
    } else {
        root->right = insertNode(root->right, x);
    }
return root;
}

A. root->left = insertNode(root->left, x);

B. root = insertNode(root->left, x);

C. root->right = insertNode(root->left, x);

D. insertNode(root->left, x);

【解析】BST 中比根小的值要插入左子树,递归结果必须接回 root->left,否则新结点与树失去联系,插入失败。B 把返回值赋给 root 会破坏根指针;C 参数与赋值位置矛盾(递归左子树却接到右边);D 丢弃了返回值,子树根结点没被更新。

【答案】A

  1. 给定一个整数数组 a,每个元素表示一个位置上的数值。要求从数组中选择若干个元素,使得任意两个被选择的元素在原数组中都不相邻,并且所选元素的总和最大。函数 choose(vector& a) 返回能够得到的最大总和,则横线处应填写( )。
intchoose(vector<int>& a){
if (a.empty()) return0;
int n = a.size();
if (n == 1) return a[0];
vector<int> dp(n, 0);
    dp[0] = a[0];
    dp[1] = max(a[0], a[1]);
for (int i = 2; i < n; ++i) {
        dp[i] = __________________________;
    }
return dp[n - 1];
}

A. dp[i - 1] + a[i]

B. max(dp[i - 1], dp[i - 2] + a[i])

C. max(dp[i - 2], a[i])

D. dp[i - 1] + dp[i - 2]

【解析】这就是"打家劫舍"模型。对第 i 个元素只有两种决策:不选它,答案为 dp[i−1];选它,则第 i−1 个不能选,答案为 dp[i−2] + a[i]。取较大者,即 max(dp[i - 1], dp[i - 2] + a[i])。

【答案】B

  1. 下面代码实现 0/1 背包的一维动态规划。第 i 个物品重量为 wt[i],价值为 val[i],背包容量为 W。横线处应填写( )。
intknapsack(int W, vector<int>& wt, vector<int>& val){
int n = wt.size();
vector<int> dp(W + 1, 0);
for (int i = 0; i < n; ++i) {
for (int w = W; w >= wt[i]; --w) {
            __________________________
        }
    }
return dp[W];
}

A. dp[w] = max(dp[w], dp[w - wt[i]] + val[i]);

B. dp[w] = max(dp[w - 1], dp[w - wt[i]] + val[i]);

C. dp[w] = dp[w] + val[i];

D. dp[w - wt[i]] = max(dp[w], val[i]);

【解析】0/1 背包的状态转移:dp[w] 表示容量 w 内能取得的最大价值,对第 i 件物品有"不放"与"放"两种选择,即 dp[w] = max(dp[w], dp[w - wt[i]] + val[i])。内层容量倒序枚举正是为了保证 dp[w - wt[i]] 仍是上一轮的状态,每件物品最多选一次。B 改错了转移基准,C 变成无条件累加(不判断容量与价值取舍),D 下标写反、赋值目标也错了。

【答案】A

二、判断题(共 10 题,每题 2 分,共计 20 分)

  1. C++ 中构造函数可以声明为虚函数,从而实现运行时多态。

A. 正确

B. 错误

【解析】构造函数不能是虚函数。虚函数机制依赖对象已构造完成的结构(虚函数表指针 vptr),而构造函数执行时对象尚未构造完毕,无法进行动态绑定。只有析构函数可以声明为虚函数。

【答案】错误

  1. 通过指向 Base 的指针删除 Derived 对象时,一定会先调用 Derived 的析构函数,再调用 Base 的析构函数。

A. 正确

B. 错误

【解析】只有当 Base 的析构函数是虚函数时,delete p 才会先调用 Derived 的析构函数再调用 Base 的析构函数;若基类析构函数非虚,则只会调用 Base 的析构函数,派生类部分的资源无法释放(属于未定义行为)。题目说"一定",过于绝对,错误。

【答案】错误

  1. 在 C++ STL 中,stack 的 pop() 函数会返回栈顶元素并将其删除。

A. 正确

B. 错误

【解析】STL 中 stack::pop() 的返回类型是 void,它只负责删除栈顶元素,不返回该元素。要取出栈顶元素需要先调用 top(),再调用 pop()。

【答案】错误

  1. 程序运行后会输出 2。
#include<iostream>
usingnamespacestd;
intmain(){
queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    q.pop();
cout << q.front() << endl;
return0;
}

A. 正确

B. 错误

【解析】queue 先进先出:依次入队 1、2、3;pop() 删除队首元素 1,此时队首变为 2;front() 返回 2。程序输出 2,说法正确。

【答案】正确

  1. 下列函数试图将整数 x 插入到一棵二叉搜索树中。假设二叉搜索树满足如下性质:对于任意结点,左子树中所有结点的值均小于该结点的值,右子树中所有结点的值均大于或等于该结点的值。判断该函数是否能够在插入后保持二叉搜索树性质。
structTreeNode {
int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* insertNode(TreeNode* root, int x){
if (root == nullptr) {
returnnew TreeNode(x);
    }
if (x < root->val) {
        root->right = insertNode(root->right, x);
    } else {
        root->left = insertNode(root->left, x);
    }
return root;
}

A. 正确

B. 错误

【解析】判断分支完全写反了:x < root->val(较小值)应该插入左子树,x >= root->val 应该插入右子树。现在把小于根的值插到了右子树、大于等于根的值插到了左子树,直接破坏 BST 的有序性质(对该树中序遍历将得不到非递减序列)。

【答案】错误

  1. 哈夫曼编码一定唯一,只要字符频率相同,得到的编码也一定完全相同。

A. 正确

B. 错误

【解析】哈夫曼编码不唯一。当出现权值相同的结点时,合并顺序(谁左谁右、先合并哪一对)可以不同;左右子树分支标 0/1 也可交换,都会得到 WPL 相同但码字不同的方案。故"编码一定完全相同"错误。

【答案】错误

  1. 若用数组按层序存储完全二叉树,且根节点下标为 0,则下标为 i 的节点左孩子下标为 2 * i + 1,右孩子下标为 2 * i + 2。

A. 正确

B. 错误

【解析】根下标从 0 开始时的标准关系:i 的左孩子为 2i + 1,右孩子为 2i + 2,父结点为 ⌊(i − 1) / 2⌋。(若根从下标 1 开始,则是 2i 与 2i + 1。)

【答案】正确

  1. 以下代码可以正确地按层换行输出二叉树的节点值。
voidprintByLevel(TreeNode* root){
if (!root) return;
queue<TreeNode*> q;
    q.push(root);
while (!q.empty()) {
for (int i = 0; i < q.size(); ++i) {
            TreeNode* cur = q.front();
            q.pop();
cout << cur->val << " ";
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
        }
cout << endl;
    }
}

A. 正确

B. 错误

【解析】内层循环的终止条件 i < q.size() 每次迭代都会**重新计算 q.size()**,而循环体内又不断把下一层的孩子入队,队列长度持续变大,导致本层循环会"顺带"处理下一层甚至更多节点,无法按层正确换行。正确写法应先把本层节点数缓存下来,如 int sz = q.size(); 再用 i < sz。

【答案】错误

  1. 使用栈非递归实现二叉树前序遍历时,若希望先访问左子树,通常应先将右孩子入栈,再将左孩子入栈。

A. 正确

B. 错误

【解析】栈是后进先出,要让左孩子先被弹出访问,就必须先压右孩子、再压左孩子,这样左孩子位于栈顶。该说法正确。

【答案】正确

  1. 动态规划问题通常要求具有最优子结构,并且常常存在重叠子问题。

A. 正确

B. 错误

【解析】动态规划适用的两个基本条件是:最优子结构(问题的最优解包含子问题的最优解,保证状态转移的正确性)和重叠子问题(同一子问题被多次求解,因此需要记忆化/表格化避免重复计算)。说法正确。

【答案】正确

三、编程题

1. 条形蛋糕

题目描述

输入格式

输出格式

一行一个正整数,表示最大总销售价格。

4
1 5 8 9
10
10
1 5 8 9 10 17 17 20 24 30
30

说明/提示

题目分析

  1. 状态定义

设  表示:长度为  的蛋糕切割后能获得的最大总售价。

  1. 初始状态

长度为 0 的蛋糕收益为 0。

  1. 状态转移

对于长度为  的蛋糕,可以枚举第一段的长度  ():

  • 第一段直接卖,获得 
  • 剩余长度  继续最优切割,获得 

因此:

参考程序

#include<bits/stdc++.h>
usingnamespacestd;

constint N = 1010;

int n;
int p[N], f[N];

intmain(){

cin >> n;

for(int i = 1; i <= n; i++) cin >> p[i];

for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++)
   f[i] = max(f[i], f[i - j] + p[j]);
 }

cout << f[n];

return0;
}

2. 满二叉树

题目描述

输入格式

第一行,一个正整数 ,表示有根二叉树结点数量。

接下来  行,每行两个非负整数 ,表示结点  的左儿子编号和右儿子编号,整数之间以空格分隔。

输出格式

输出一行,一个整数,表示所有子树中满二叉树的数量。

4
2 3
4 0
0 0
0 0
2
3
2 3
0 0
0 0
3

说明/提示

数据范围

对于  的测试点,保证 。

对于所有测试点,保证 。

题目分析

  • 左子树必须是满二叉树。
  • 右子树必须是满二叉树。
  • 左子树的深度 = 右子树的深度。

参考程序

#include<bits/stdc++.h>
usingnamespacestd;
constint N = 100010;
int L[N], R[N];
int n, res;

intdfs(int u){
if (u == 0) return0;
int hl = dfs(L[u]);
int hr = dfs(R[u]);
if (hl != -1 && hl == hr) {
        res++;
return hl + 1;
    }
return-1;
}

intmain(){

cin >> n;
for (int i = 1; i <= n; i ++)
cin >> L[i] >> R[i];

    dfs(1);
cout << res;

return0;
}

相关学习资料