欢迎光临
我们一直在努力

GESP2026年9月认证C++六级( 第二部分判断题(1~10题)精讲



第1题:构造函数——编译器会自动送我们一个无参数构造函数吗?

题目

题目给出:

class Student {
public:
Student(int x) {
age = x;
}

private:
int age;
};

int main() {
Student s;
}

判断:

下列代码可以正常编译,因为编译器会自动为 Student 类生成一个无参数构造函数。

正确答案:错误(×)。


一、先认识构造函数

同学们,假设我们正在制作一个学生信息管理系统。

每个学生对象都有一个年龄:

int age;

我们希望创建学生时,就给他设置好年龄。

于是编写了构造函数:

Student(int x) {
age = x;
}

例如:

Student s(10);

表示创建一个学生对象,并把年龄设置为10。

注意,这个构造函数需要一个整数参数:

int x

因此,它是一个带参数的构造函数。


二、编译器什么时候会自动生成无参数构造函数?

如果我们没有声明任何构造函数,编译器通常会为类隐式声明一个默认构造函数。

例如:

class Student {
public:
int age;
};

这时:

Student s;

可以正常编译。

但是,本题不一样!

我们已经自己写了一个构造函数:

Student(int x)

此时,编译器不会再自动为我们生成无参数构造函数。

所以:

Student s;

找不到合适的构造函数,编译会失败。


三、怎样修改才能正常编译?

方法一:创建对象时传入参数。

Student s(10);

方法二:自己添加无参数构造函数。

class Student {
public:
Student() {
age = 0;
}

Student(int x) {
age = x;
}

private:
int age;
};

这样,两种写法都可以:

Student s1;
Student s2(10);


本题记忆口诀

自己写了带参数构造函数,编译器就不会自动补上无参数构造函数。



第2题:继承与访问权限——孩子能随便打开父亲的私人保险箱吗?

题目

class Base {
private:
int value = 10;
};

class Child : public Base {
public:
int get() {
return value;
}
};

判断:

下列代码合法,因为派生类可以直接访问基类的私有成员 value。

正确答案:错误(×)。


一、理解 private

想象基类 Base 是一位收藏家。

他有一个私人保险箱:

private:
int value = 10;

private 就像保险箱的密码锁。

这个成员只能由 Base 类自己的成员函数直接访问。

即使 Child 继承了 Base,也不能直接访问这个私有成员。

所以:

return value;

在 Child 的成员函数中是不合法的。


二、继承是不是把所有东西都变成 public?

不是!

继承并不意味着派生类可以随意访问基类的所有成员。

要特别区分:

基类成员权限派生类能否直接访问
public 可以
protected 可以
private 不可以

注意:这里讨论的是派生类成员函数能否直接访问基类成员,而不是对象是否继承了相应的基类部分。


三、怎样修改?

可以把 private 改为 protected:

class Base {
protected:
int value = 10;
};

class Child : public Base {
public:
int get() {
return value;
}
};

也可以保留 private,在基类中提供一个公共接口:

class Base {
private:
int value = 10;

public:
int getValue() {
return value;
}
};

class Child : public Base {
public:
int get() {
return getValue();
}
};


本题记忆口诀

继承不是万能钥匙,private 仍然是私人区域。



第3题:队列——出队的是谁?

题目

queue<int> q;

q.push(10);
q.push(20);
q.push(30);

q.pop();

cout << q.front();

判断:

下列代码执行后,输出结果为30。

正确答案:错误(×)。


一、队列像排队买票

队列遵循:

先进先出(FIFO)。

谁先进入队列,谁就先离开。

依次执行:

q.push(10);
q.push(20);
q.push(30);

队列变成:

队头 队尾
↓ ↓
┌────┬────┬────┐
│ 10 │ 20 │ 30 │
└────┴────┴────┘


二、执行 pop()

q.pop();

队列删除的是队头元素,也就是最先进入的 10。

删除后:

队头 队尾
↓ ↓
┌────┬────┐
│ 20 │ 30 │
└────┴────┘


三、执行 front()

cout << q.front();

front() 读取队头元素。

现在队头是 20。

因此输出:

20

不是30。

本题记忆口诀

队列先进先出,pop 删除队头,front 读取队头。



第4题:完全二叉树的数组存储——左孩子住在哪个位置?

题目

判断:

一棵完全二叉树按照从上到下、从左到右的顺序,将节点依次存储在数组 tree[1]、tree[2]、……中。若节点 tree[i] 存在左孩子,则其左孩子存储在 tree[2 * i] 中。

正确答案:正确(√)。


一、先画一棵完全二叉树

例如:

A
/ \\
B C
/ \\ /
D E F

如果按照从上到下、从左到右的顺序存储:

数组下标节点
tree[1] A
tree[2] B
tree[3] C
tree[4] D
tree[5] E
tree[6] F

我们发现:

  • A 的左孩子 B 在下标2。

  • B 的左孩子 D 在下标4。

  • C 的左孩子 F 在下标6。


二、找出规律

对于下标为 i 的节点:

左孩子的下标是:

2 * i

右孩子的下标是:

2 * i + 1

例如,节点 B 在:

tree[2]

它的左孩子就在:

tree[2 * 2]

也就是:

tree[4]

正好是 D。

因此,题目中的说法正确。


本题记忆口诀

完全二叉树从1开始编号:左孩子2i,右孩子2i+1。

注意:这套公式依赖题目规定的数组存储方式和从1开始的编号。



第5题:二叉搜索树的中序遍历——为什么结果是从小到大的?

题目

代码:

void inorder(TreeNode *root) {
if (!root)
return;

inorder(root->left);

cout << root->val << " ";

inorder(root->right);
}

判断:

对任意一棵二叉搜索树执行中序遍历,得到的关键字序列一定是非递减的。

正确答案:正确(√)。


一、二叉搜索树的规则

二叉搜索树 BST 的基本性质是:

  • 左子树的关键字小于当前节点的关键字。

  • 右子树的关键字大于当前节点的关键字。

这里先按照关键字互不相同的常见情况理解。

例如:

8
/ \\
3 10
/ \\ \\
1 6 14

左边比根小,右边比根大。


二、中序遍历的规则

中序遍历:

左 → 根 → 右

我们先访问左子树。

左子树中的关键字都比根节点小。

然后访问根节点。

最后访问右子树。

右子树中的关键字都比根节点大。

而且左右子树内部也遵循同样的规则。

因此,中序遍历会按照关键字从小到大的顺序访问节点。

上面这棵树的中序遍历结果是:

1 3 6 8 10 14

这就是一个递增序列。

如果 BST 的定义允许重复关键字,并且重复值的放置规则也符合相应的搜索树性质,中序结果则是非递减序列,即允许相等。


本题记忆口诀

二叉搜索树中序遍历:左边小,根居中,右边大,结果有序。



第6题:BFS广度优先搜索——为什么第一次到达就是最短路?

题目

代码:

vector<int> tree[100];
bool visited[100];
int dist[100];

void search(int start) {
queue<int> q;

q.push(start);
visited[start] = true;
dist[start] = 0;

while (!q.empty()) {
int u = q.front();
q.pop();

for (int v : tree[u]) {
if (!visited[v]) {
visited[v] = true;
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
}

判断:

若使用下列代码从节点 start 开始访问一棵树,则第一次到达某个节点时所经过的边数,一定是从 start 到该节点的最少边数。

正确答案:正确(√)。


一、先理解 BFS

BFS 的中文名称是:

广度优先搜索。

我们可以把它想象成往池塘里扔一颗小石子。

水波会一圈一圈向外扩散:

第0层:起点
第1层:距离起点1条边的节点
第2层:距离起点2条边的节点
第3层:距离起点3条边的节点

BFS 使用队列,按照发现节点的先后顺序处理它们。


二、为什么第一次到达就是最短的?

假设从 A 出发:

A
/ \\
B C
/ \\
D E

BFS 首先访问 A。

然后发现 B、C。

再访问 B、C,发现 D、E。

访问顺序是:

A → B → C → D → E

距离分别是:

节点从 A 出发的最少边数
A 0
B 1
C 1
D 2
E 2

BFS 是按距离一层一层扩展的。

因此,在无权图中,节点第一次被发现时,就已经通过最少边数的路径到达了它。

本题讨论的是树。树中任意两个节点之间只有一条简单路径,因此从起点到某个节点的路径本身也是唯一的。

代码中:

dist[v] = dist[u] + 1;

就是在记录从起点到新发现节点的距离。


本题记忆口诀

BFS像水波,一层一层扩散;无权图中,第一次发现就是最短距离。

注意:这里的最短距离指的是边数最少。如果边有不同的长度或代价,就不能直接套用这个结论。



第7题:哈夫曼编码——频率高的字符,编码一定更短吗?

题目

判断:

哈夫曼编码的生成过程基于贪心算法,出现频率越高的字符,其编码长度一定不会比出现频率更低的字符更长。

正确答案:正确(√)。


一、先理解编码长度

想象我们要给一组字符设计二进制编码。

每个字符对应一条从根节点走到叶子节点的路径。

例如:


/ \\
0 1

如果一个字符对应的路径只有1条边,那么它的编码长度就是1。

如果路径有3条边,那么编码长度就是3。

在哈夫曼编码中,我们希望出现频率高的字符尽量使用较短的编码,从而降低整体的带权路径长度。


二、为什么高频字符不会比低频字符更长?

假设有两个字符:

  • 字符 A 的频率是10。

  • 字符 B 的频率是2。

假设某个编码树中:

  • A 的编码长度是4。

  • B 的编码长度是2。

那么它们对 WPL 的贡献分别是:

A:10 × 4 = 40
B: 2 × 2 = 4

如果交换 A、B 在树中的位置:

A:10 × 2 = 20
B: 2 × 4 = 8

总贡献从:

40 + 4 = 44

变成:

20 + 8 = 28

WPL 变小了。

所以,原来的安排不可能是最优的哈夫曼编码。

一般来说,如果一个字符频率更高,却拥有更长的编码,那么交换它与较低频字符的编码位置,就会使 WPL 下降,与哈夫曼编码的最优性矛盾。

因此,在最优哈夫曼编码中,频率较高的字符不会比频率较低的字符拥有更长的编码。


本题记忆口诀

哈夫曼追求总成本最小:高频字符不能被安排得比低频字符更深。



第8题:格雷编码——任意两个编码都只差一位吗?

题目

判断:

在 n 位格雷码中,任意两个编码之间都只相差一个二进制位。

正确答案:错误(×)。


一、格雷编码的真正性质

格雷编码有一个重要特点:

按照格雷编码的排列顺序,相邻的两个编码恰好只有一位不同。

例如,2位格雷码:

00
01
11
10

比较相邻编码:

00 → 01

只有最后一位不同。

01 → 11

只有第一位不同。

11 → 10

也只有一位不同。


二、题目错在哪里?

题目说的是:

任意两个编码之间都只相差一个二进制位。

“任意两个”比“相邻两个”范围大得多。

例如:

00
11

这两个编码相差两位:

00
11
↑↑

所以,任意两个格雷编码之间不一定只差一位。

题目把“相邻编码”偷换成了“任意编码”,因此错误。


本题记忆口诀

格雷码只保证相邻编码差一位,不保证任意两个编码都差一位。



第9题:完全背包——为什么容量从小到大,就能重复选择物品?

题目

代码:

for (int i = 0; i < n; ++i) {
for (int w = weight[i]; w <= W; ++w) {
dp[w] = max(dp[w],
dp[w – weight[i]] + value[i]);
}
}

判断:

下列一维动态规划代码实现的是完全背包问题,因为在处理第 i 种物品时,同一种物品可能被重复选择。

正确答案:正确(√)。


一、先认识完全背包

想象你有一个魔法背包,容量是 W。

现在有一些种类的物品,每种物品都有:

  • 重量 weight[i]。

  • 价值 value[i]。

完全背包允许:

每种物品可以选择任意多次,只要背包容量允许。

例如,有一种重量为2、价值为3的宝石。

如果背包容量为6,那么我们可以选择:

1颗:重量2,价值3
2颗:重量4,价值6
3颗:重量6,价值9

因此,同一种物品可以重复选择。


二、代码为什么使用从小到大的循环?

关键代码:

for (int w = weight[i]; w <= W; ++w)

容量从小到大枚举。

转移公式:

dp[w] = max(dp[w],
dp[w – weight[i]] + value[i]);

假设:

物品重量 = 2
物品价值 = 3

当计算:

w = 2

我们可以选择一颗宝石:

dp[2] = 3

接着计算:

w = 4

这时:

dp[4] = max(dp[4], dp[2] + 3);

由于 dp[2] 已经在本轮更新为3,所以:

dp[4] = 6

这相当于选择了两颗宝石。

继续计算:

w = 6

此时:

dp[6] = max(dp[6], dp[4] + 3);

得到:

dp[6] = 9

相当于选择了三颗宝石。

所以,从小到大更新容量,允许当前物品的最新状态继续参与后面的计算,从而实现重复选择。


本题记忆口诀

完全背包容量正序,可以重复选择;0/1背包容量倒序,避免重复选择。



第10题:斐波那契递归——代码简短,运行就一定快吗?

题目

int fib(int n) {
if (n <= 1)
return n;

return fib(n – 1) + fib(n – 2);
}

判断:

下列递归程序能得到正确的斐波那契数,其时间复杂度和空间复杂度都是 O(n)。

正确答案:错误(×)。


一、先认识斐波那契数列

斐波那契数列规定:

fib(0) = 0
fib(1) = 1

从第三项开始:

fib(n) = fib(n – 1) + fib(n – 2)

例如:

0、1、1、2、3、5、8、13……

题目中的程序正是按照这个定义编写的。

因此,对于正常的非负整数输入,它能够计算正确的斐波那契数。

但是,程序能够算对,不代表它运行得快。


二、为什么时间复杂度不是 O(n)?

看这句:

return fib(n – 1) + fib(n – 2);

为了计算 fib(n),程序需要计算:

fib(n – 1)
fib(n – 2)

而计算 fib(n – 1),又会继续计算:

fib(n – 2)
fib(n – 3)

于是,许多相同的子问题被重复计算。

例如:

fib(5)
├── fib(4)
│ ├── fib(3)
│ └── fib(2)
└── fib(3)
├── fib(2)
└── fib(1)

你发现了吗?

fib(3) 被计算了不止一次,fib(2) 也被计算了不止一次。

当 n 增大时,递归调用的数量会快速增长。

所以,这种朴素递归的时间复杂度是指数级的,常用上界表示为:

O(2^n)


三、空间复杂度为什么是 O(n)?

虽然程序会产生很多递归调用,但这些调用不一定全部同时留在内存中。

程序执行:

fib(n – 1)

时,会先深入计算这条递归分支。

递归调用需要等待下层函数返回,因此函数调用栈会逐层增加。

最深时,调用层数与 n 成正比。

所以递归调用栈所需要的额外空间是:

O(n)

因此:

复杂度正确结果
时间复杂度 指数级,常用上界表示为 O(2ⁿ)
空间复杂度 O(n)

题目说时间复杂度和空间复杂度都是 O(n),由于时间复杂度不正确,所以整句话错误。


本题记忆口诀

朴素斐波那契递归:重复计算很多次,时间指数级;递归深度与 n 成正比,栈空间 O(n)。


第二部分判断题1~10题答案汇总

题号正确答案核心知识点
1 × 自定义带参数构造函数后,不会自动生成无参数构造函数
2 × 派生类不能直接访问基类的 private 成员
3 × 队列先进先出,pop 删除队头,front 读取队头
4 完全二叉树数组存储:左孩子下标为 2i
5 二叉搜索树中序遍历得到非递减序列
6 BFS 在无权图中按距离逐层扩展,第一次发现即为最短距离
7 最优哈夫曼编码中,高频字符不会比低频字符拥有更长编码
8 × 格雷码只保证相邻编码相差一位
9 完全背包容量正序更新,允许重复选择物品
10 × 朴素斐波那契递归时间复杂度为指数级,空间复杂度为 O(n)

给同学们的最后提醒:判断题最怕三个字——“想当然”

做判断题时,建议大家养成三个习惯:

第一,遇到“自动生成”,先检查有没有特殊条件。

例如,类中已经声明了带参数构造函数,就不能想当然地认为编译器还会自动提供无参数构造函数。

第二,遇到“一定”“任意”,主动寻找反例。

例如,格雷编码相邻的编码只差一位,但任意两个编码不一定只差一位。

第三,遇到算法复杂度,不要只看代码有几行。

例如,斐波那契递归只有几行代码,却会产生大量重复计算。代码短,不代表运行快。

真正的编程能力,不是看到熟悉的代码就立刻选答案,而是能够解释:为什么正确?为什么错误?有没有反例?

把这三个习惯练好,判断题就不再只是猜对错,而会成为检验我们是否真正理解 C++ 和算法的好机会!


赞(0)
未经允许不得转载:171主机测评 » GESP2026年9月认证C++六级( 第二部分判断题(1~10题)精讲
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址