试卷解析 · 理解每一步
2025-03-Level-6
2025-03-Level-6
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
在面向对象编程中,类是一种重要的概念。下面关于类的描述中,不正确的是()。
正确答案D
解析详情
类是抽象模板,对象是具体实例。类定义后可以通过继承进行扩展(子类增加属性/方法)或通过多态重写方法,D项称“不能修改或扩展”违背了面向对象的开放封闭原则。
第 2 题(单选题,2 分)
哈夫曼编码是一种数据压缩算法。以下关于哈夫曼编码的描述中,不正确的是()。
正确答案B
解析详情
哈夫曼树构造时,权值越大的节点越靠近根节点(路径越短),从而使总带权路径长度最小。B项称“频率越低离根越近”与贪心合并原则相反。
第 3 题(单选题,2 分)
以下代码实现了树的哪种遍历方式?
void traverse(TreeNode* root) {
if (root == nullptr) return;
cout << root->val << " ";
traverse(root->left);
traverse(root->right);
}正确答案A
解析详情
代码先输出当前节点的值 `cout << root->val`,再递归访问左子树和右子树,遵循“根-左-右”的顺序,符合前序遍历定义。
第 4 题(单选题,2 分)
以下关于完全二叉树的代码描述,正确的是()。
bool isCompleteTree(TreeNode* root) {
if (root == nullptr) return true;
queue<TreeNode*> q;
q.push(root);
bool hasNull = false;
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
if (node == nullptr) {
hasNull = true;
} else {
if (hasNull) return false;
q.push(node->left);
q.push(node->right);
}
}
return true;
}正确答案B
解析详情
代码通过队列 BFS 遍历,用 `hasNull` 记录是否出现过空节点。完全二叉树要求节点连续填充,若遇到空节点后再次出现非空节点,则不是完全二叉树。
第 5 题(单选题,2 分)
以下代码实现了二叉排序树的哪种操作?
TreeNode* op(TreeNode* root, int val) {
if (root == nullptr) return new TreeNode(val);
if (val < root->val) {
root->left = op(root->left, val);
} else {
root->right = op(root->right, val);
}
return root;
}正确答案B
解析详情
代码中 `new TreeNode(val)` 表明在找到空位置时创建新节点,并根据 `val` 与当前节点值的大小关系决定向左或向右递归挂载。这是典型的 BST 插入过程。
第 6 题(单选题,2 分)
给定字符集 {A, B, C, D} 的出现频率分别为 {5, 1, 6, 2},则正确的哈夫曼编码是()。
正确答案B
解析详情
合并过程:1.合并最小的B(1)和D(2)得3;2.合并新节点3和A(5)得8;3.合并8和C(6)得14。由此得路径长度:C(1位), A(2位), B和D(3位)。选项B符合此长度规律。
第 7 题(单选题,2 分)
关于动态规划的描述,正确的是()。
正确答案B
解析详情
动态规划核心特征:1.最优子结构(原问题最优解包含子问题最优解);2.重叠子问题(子问题被反复计算)。不重叠子问题是分治法的特征,而非动态规划。
第 8 题(单选题,2 分)
以下代码中,类的构造函数被调用了()次。
class MyClass {
public:
MyClass() {
cout << "Constructor called!" << endl;
}
};
int main() {
MyClass obj1;
MyClass obj2 = obj1;
return 0;
}正确答案A
解析详情
`MyClass obj1;` 调用默认构造函数,输出一次。`MyClass obj2 = obj1;` 属于拷贝初始化,调用的是拷贝构造函数,而非默认构造函数,因此不会产生该输出。
第 9 题(单选题,2 分)
以下代码实现了循环队列的哪种操作?
class CircularQueue {
int* arr;
int front, rear, size;
public:
CircularQueue(int k) {
size = k;
arr = new int[k];
front = rear = -1;
}
bool enQueue(int value) {
if (isFull()) return false;
if (isEmpty()) front = 0;
rear = (rear + 1) % size;
arr[rear] = value;
return true;
}
};
}正确答案A
解析详情
代码中 `rear = (rear + 1) % size` 实现了尾指针在数组中的循环后移,随后 `arr[rear] = value` 存入新数据,这符合循环队列入队操作的特征。
第 10 题(单选题,2 分)
以下代码实现了二叉树的深度优先搜索(DFS),并统计叶子结点的数量,则横线上应填写()。
int countLeafNodes(TreeNode* root) {
if (root == nullptr) return 0;
stack<TreeNode*> s;
s.push(root);
int count = 0;
while (!s.empty()) {
TreeNode* node = s.top();
s.pop();
if (node->left == nullptr && node->right == nullptr) {
count++;
}
if (node->right) s.push(node->right);
// 在此处填入代码
}
return count;
}正确答案A
解析详情
非递归 DFS 利用栈实现,访问完父节点后需将其子节点压入栈中。代码已将右孩子入栈,横线处应补充左孩子的入栈逻辑,即 `if (node->left) s.push(node->left);`。
第 11 题(单选题,2 分)
以下代码实现了二叉树的广度优先搜索(BFS),并查找特定值的节点,则横线上应填写()。
TreeNode* findNode(TreeNode* root, int target) {
if (root == nullptr) return nullptr;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* current = q.front();
q.pop();
if (current->val == target) {
return current; // 找到目标节点
}
// 在此处填入代码
}
return nullptr;
}if (current->left) q.push(current->left);
if (current->right) q.push(current->right);if (current->left) q.pop(current->left);
if (current->right) q.pop(current->right);if (current->left) q.front(current->left);
if (current->right) q.front(current->right);if (current->left) q.push(current->right);
if (current->right) q.push(current->left);正确答案A
解析详情
BFS 利用队列按层级访问节点。从队列弹出当前节点后,需将其存在的左右子节点按序加入队尾(push),以便在后续轮次中访问下一层。
第 12 题(单选题,2 分)
以下代码用于生成 n 位格雷编码。横线上应填写()。
vector<string> generateGrayCode(int n) {
if (n == 0) return {"0"};
if (n == 1) return {"0", "1"};
vector<string> prev = generateGrayCode(n - 1);
vector<string> result;
for (string s : prev) {
result.push_back("0" + s); // 在前缀添加 0
}
for (int i = prev.size() - 1; i >= 0; i--) {
// 在此处填入代码
}
return result;
}正确答案A
解析详情
构造n位格雷码常基于n-1位:先在前缀加'0',再将n-1位序列逆序排列后前缀加'1'。代码中循环从 `prev.size()-1` 递减,对应逆序加'1'的操作。
第 13 题(单选题,2 分)
以下代码实现了0/1背包问题的动态规划解法。假设物品重量为 weights[],价值为 values[],背包容量为 W,横线上应填写()。
int knapsack(int W, vector<int>& weights, vector<int>& values) {
int n = weights.size();
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
if (weights[i-1] > j) {
dp[i][j] = dp[i-1][j]; // 当前物品装不下
} else {
dp[i][j] = max(____); // 在此处填入代码
}
}
}
return dp[n][W];
}正确答案B
解析详情
当第 `i` 件物品能装下时,最大价值取“不选(`dp[i-1][j]`)”与“选中(扣除重量后的前一状态价值 `dp[i-1][j-weights[i-1]]` 加上该物价值)”的较大值。
第 14 题(单选题,2 分)
以下代码用于检查字符串中的括号是否匹配,横线上应填写()。
bool isBalanced(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) return false; // 无左括号匹配
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return _____; //在此处填入代码
}正确答案C
解析详情
括号匹配成功不仅要求遍历中每个右括号都能找到对应的左括号弹出,还要求遍历结束后栈为空(没有孤立的左括号)。故应返回 `st.empty()`。
第 15 题(单选题,2 分)
关于下面代码,说法错误的是()。
class Shape {
protected:
string name;
public:
Shape(const string &n) : name(n) {}
virtual double area() const { return 0.0; }
};
class Circle : public Shape {
private:
double radius;
public:
Circle(const string& n, double r) : Shape(n), radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width; // 宽度
double height; // 高度
public:
Rectangle(const string& n, double w, double h) : Shape(n), width(w), height(h) {}
double area() const override {
return width * height;
}
};
int main() {
Circle circle("MyCircle", 5.0);
Rectangle rectangle("MyRectangle", 4.0, 6.0);
Shape* shapePtr = &circle;
cout << "Area: " << shapePtr->area() << endl;
shapePtr = &rectangle;
cout << "Area: " << shapePtr->area() << endl;
return 0;
}正确答案A
解析详情
C++ 中基类指针可以合法地指向其派生类对象(向上转型),这是多态的核心实现手段,编译器完全支持。因此 A 项描述“出现编译错误”是错误的。
判断题
第 1 题(判断题,2 分)
哈夫曼树在构造过程中,每次合并权值最小的两个节点,最终生成的树带权路径长度最小。
正确答案正确
解析详情
哈夫曼树通过贪心策略不断合并当前权值最低的节点,确保了所有叶子节点的带权路径长度(WPL)达到最小,是理论上的最优二叉树。
第 2 题(判断题,2 分)
格雷编码的相邻两个编码之间必须有多位不同,以避免数据传输错误。
正确答案错误
解析详情
格雷码的核心设计初衷是相邻编码之间仅有“一位”不同,旨在通过减少电平跳变位来降低信号传输过程中的状态不稳定性和错误率。
第 3 题(判断题,2 分)
在树的深度优先搜索(DFS)中,使用队列作为辅助数据结构以实现“先进后出”的访问顺序。
正确答案错误
解析详情
DFS 遵循“先进后出”原则,非递归实现必须使用栈(Stack)。队列遵循“先进先出”,是实现广度优先搜索(BFS)的核心工具。
第 4 题(判断题,2 分)
以下代码实现的是二叉树的中序遍历:
void traverse(TreeNode* root) {
if (root == nullptr) return;
traverse(root->left);
cout << root->val << " ";
traverse(root->right);
}正确答案正确
解析详情
代码中的输出语句 `cout` 位于两次递归调用之间,执行顺序为:左子树 -> 根节点 -> 右子树,这完全符合中序遍历的定义。
第 5 题(判断题,2 分)
C++ 支持构造函数重载,但默认无参数的构造函数只能有一个。
正确答案正确
解析详情
函数重载要求参数列表不同。无参构造函数的签名是唯一的,因此一个类内不可能同时定义两个不带参数的默认构造函数,否则会导致调用歧义。
第 6 题(判断题,2 分)
二叉排序树(BST)中,若某节点的左子树为空,则该节点一定是树中的最小值节点。
正确答案错误
解析详情
BST 的最小值位于整棵树“最左边”的节点。仅左子树为空只能说明它是局部最小值,若该节点是父节点的右子树根,则其父节点必然更小。
第 7 题(判断题,2 分)
在动态规划解决一维硬币找零问题时,若硬币面额为 [1, 3, 4],目标金额为 6,则最少需要 2 枚硬币。
正确答案正确
解析详情
目标 6,面额 [1, 3, 4]。动态规划通过计算发现 3+3=6 仅需 2 枚硬币,而贪心策略先选 4 后需补充两个 1(总 3 枚),证明 2 是最优解。
第 8 题(判断题,2 分)
面向对象编程中,封装是指将数据和行为绑定在一起,并对外隐藏实现细节。
正确答案正确
解析详情
封装将属性和方法封装在类中,并通过访问修饰符限制外部直接访问内部数据,只暴露必要接口,极大增强了系统的安全性与解耦性。
第 9 题(判断题,2 分)
以下代码创建的树是一棵完全二叉树:
TreeNode* root = new TreeNode{1};
root->left = new TreeNode{2};
root->right = new TreeNode{3};
root->left->left = new TreeNode{4};正确答案正确
解析详情
按层序遍历该树,节点编号依次为 1(根)、2(左)、3(右)、4(2的左)。填充顺序严格连续且无空位,完全符合完全二叉树定义。
第 10 题(判断题,2 分)
栈和队列均可以用双向链表实现,插入和删除操作的时间复杂度为 O(1)。
正确答案正确
解析详情
双向链表通过维护首尾指针,支持在 O(1) 内完成两端的增删。栈(一端操作)和队列(首删尾增)均可借此实现高效的常数级复杂度操作。