面向对象编程(OOP)是一种特殊的程序设计方法。下面( )不是重要的OOP特性。
D:模块化不是 OOP 特有的重要特性;抽象(忽略细节提取共性)、封装(隐藏内部实现)、继承(代码复用)是面向对象的重要特性。
以下关于C++中类的说法,哪一项是正确的?
C:类中默认访问权限是 private,数据一般设为私有,公有成员函数是访问私有数据的途径;A 说默认 public 错,B 构造函数不能声明返回类型,D 同类的对象共享函数代码。
以下C++代码段中存在语法错误或逻辑错误,( )是正确的。
01#include <iostream> 02using namespace std; 03 04class MyClass { 05public: 06 MyClass() { 07 cout << "Constructor called!" << endl; 08 } 09 void display() { 10 cout << "Display function called!" << endl; 11 } 12}; 13 14int main() { 15 MyClass* obj = NULL; 16 obj->display(); 17 return 0; 18}
C:obj 初始化为 NULL 后直接 obj->display() 是对空指针解引用,会产生空指针访问错误;A 中 NULL 可用于指针初始化,B 用对象也可但非必须,D 描述不符。
阅读以下代码,下面哪一项是正确的?
01void processData() { 02 stack<int> s; 03 queue<int> q; 04 for (int i = 1; i <= 5; ++i) { 05 s.push(i); 06 q.push(i); 07 } 08 while (!s.empty()) { 09 cout << "Stack pop: " << s.top() << endl; 10 s.pop(); 11 } 12 while (!q.empty()) { 13 cout << "Queue pop: " << q.front() << endl; 14 q.pop(); 15 } 16}
B:栈是后进先出,1~5 依次压入后弹出顺序为 5 4 3 2 1;队列是先进先出,弹出顺序为 1 2 3 4 5,二者输出顺序相反。
N 个节点的双向循环链,在其中查找某个节点的平均时间复杂度是()。
B:O(N)。链表不支持随机访问,查找须从头逐结点比较,双向循环链表平均仍要查约 N/2 个结点,平均时间复杂度 O(N)。
以下关于树的说法,( )是正确的。
B:满二叉树第 k 层结点数为 2^(k-1),故每层结点数等于 O(2^(层数-1));叶子结点度为 0 而非 2,A 错;结点度之和等于边数两倍,C 错;先序与中序结果通常不同,D 错。
已知字符集 {A, B, C, D} 的出现频率如下表所示:
| 字符 | 频率 |
|---|---|
| A | 8 |
| B | 3 |
| C | 1 |
| D | 6 |
根据哈夫曼编码法,下面( )是正确的哈夫曼树。
A:频率 C1、B3 先合并为 4,再与 D6 合并为 10,最后与 A8 合并为 18,得到根 ABCD 左 A 右 BCD、BCD 左 D 右 BC、BC 左 B 右 C 的树。
已知字符集 {A, B, C, D} 的出现频率为 A:8、B:3、C:1、D:6,根据哈夫曼编码法,各字符的哈夫曼编码是( )。
C:按合并 C+B=4、(C+B)+D=10、A+10=18 建树,左 0 右 1 得 A:0、D:11、C:100、B:101,与选项 C 完全一致,且互为前缀码。
( )是 位格雷编码。
A:格雷码相邻编码仅一位不同,000→001→011→010→110→111→101→100 每步只翻转一位;B 是二进制自然序,C、D 存在多位突变。
根据下面二叉树和给定的代码,给定以下二叉搜索树,调用函数 search(root, 7) 时,输出的结果是()。
01#include <iostream> 02using namespace std; 03 04struct TreeNode { 05 int val; 06 TreeNode* left; 07 TreeNode* right; 08 TreeNode(int x) : val(x), left(NULL), right(NULL) {} 09}; 10 11TreeNode* search(TreeNode* root, int val) { 12 cout << root->val << " "; 13 if (root == NULL || root->val == val) return root; 14 15 if (val < root->val) 16 return search(root->left, val); 17 else 18 return search(root->right, val); 19}
二叉搜索树根为 ,第二层为 和 ,第三层为 、、、。
B:search 先输出当前结点再比较:从根 5 输出 5,7>5 转右子树;在 7 输出 7 后命中返回,故输出 5 7。
阅读以下二叉树的深度优先搜索算法,横线上应填写()。
01void dfs(TreeNode* root) { 02 if (root == nullptr) 03 return; 04 05 stack<TreeNode*> s; 06 s.push(root); 07 while (!s.empty()) { 08 ____________ // 在此处填入代码 09 cout << node->value << " "; 10 11 if (node->right) s.push(node->right); 12 if (node->left) s.push(node->left); 13 } 14}
B:非递归 DFS 用栈,先取栈顶 TreeNode* node=s.top() 再 s.pop() 弹出,随后输出并压入右、左孩子;栈无 front 操作,A 不弹出会死循环。
阅读以下二叉树的广度优先搜索的代码,横线上应填写()。
01#include <queue> 02void bfs(TreeNode* root) { 03 if (root == NULL) return; 04 05 queue<TreeNode*> q; 06 q.push(root); 07 while (!q.empty()) { 08 ____________ // 在此处填入代码 09 cout << node->val << " "; 10 if (node->left) { 11 q.push(node->left); 12 } 13 if (node->right) { 14 q.push(node->right); 15 } 16 } 17}
D:BFS 用队列,取队首 q.front() 并 q.pop() 出队后输出,再入队左右孩子;栈才用 top,A、B 对队列无效。
使用宽度优先搜索(BFS)遍历以下这棵树,可能的输出是( )。
1 / \ 2 3 / \ \ 8 9 6 / \ \ 4 5 7
C:BFS 按层输出:第 1 层 1,第 2 层 2 3,第 3 层 8 9 6,第 4 层 4 5 7,合并得 1 2 3 8 9 6 4 5 7。
以下关于动态规划的描述,( )是正确的。
B:动态规划要求最优子结构(最优解由子问题最优解构成)和无后效性(当前决策只影响后续);A 说不需要重叠子问题错,C 说通常递归实现不准确,D 表述混乱。
假设背包的最大容量 ,共有 个物品可供选择, 个物品重量分别为 weights = [2, 3, 5, 7],价值 values = [30, 40, 60, 80],则该0/1背包问题中最大价值为()。
C:100。容量 8 下枚举组合:2+3+5=10 超重,3+5=8 得 40+60=100 最大;2+5=7 得 90、单取 7 得 80,故最大价值为 100。
构造函数是一种特殊的类成员函数,构造函数的名称和类名相同。但通过函数重载,可以创建多个同名的构造函数,条件是每个构造函数的参数列表不同。
A:正确。构造函数名与类名相同、无返回类型,是特殊的类成员函数;依据函数重载规则,参数列表不同的多个构造函数可以共存于同一类中。
类的静态成员函数既能访问类的静态数据成员,也能访问非静态成员数据。
B:错误。静态成员函数没有 this 指针,不依附于具体对象,无法访问属于对象的非静态成员,只能访问静态成员,故题述不成立。
栈中元素的插入和删除操作都在栈的顶端进行,所以方便用单向链表实现。
A:正确。栈的插入与删除都发生在栈顶,单向链表在表头做头插、头删都是 O(1),无需前驱指针,实现简单方便,故用单向链表合适。
下面代码构建的树一定是完全二叉树:
01struct TreeNode { 02 int value; 03 TreeNode* left; 04 TreeNode* right; 05}; 06 07TreeNode* buildCompleteBinaryTree() { 08 TreeNode* root = new TreeNode{1}; 09 root->left = new TreeNode{2}; 10 root->right = new TreeNode{3}; 11 root->left->left = new TreeNode{4}; 12 root->left->right = new TreeNode{5}; 13 root->right->left = new TreeNode{6}; 14 return root; 15}
A:正确。层序为 1,2,3,4,5,6:前两层排满,第三层 4、5、6 从左到右连续,6 是 3 的左孩子且 3 无右孩子,满足完全二叉树定义。
在二叉排序树中,左子树所有节点的值都大于根节点的值,右子树所有节点的值都小于根节点的值。
B:错误。关系说反了:二叉排序树中,左子树所有结点的值都小于根结点的值,右子树所有结点的值都大于根结点的值,与题述相反。
在生成一个派生类的对象时,只调用派生类的构造函数。
B:错误。创建派生类对象时会先调用基类构造函数,再调用派生类构造函数,两个都会执行;析构时顺序相反,先派生类后基类。,先派生类后基类。
下面的代码实现了二叉树的前序遍历,它通过递归方法访问每个节点并打印节点值。
01void preorder(TreeNode* root) { 02 if (root == NULL) return; 03 cout << root->val << " "; 04 preorder(root->left); 05 preorder(root->right); 06}
A:正确。函数先输出根结点值,再递归遍历左子树、右子树,是根左右的前序遍历,通过递归方式访问并打印每个结点。,通过递归访问每个结点。
在二叉树中,宽度优先搜索算法(BFS)保证从起点到每个节点的访问路径是边数最少的路径(即最短路径)。
A:正确。BFS 按层逐层扩展,第一次访问到某结点时经过的边数必然最少,因此在无权图中 BFS 保证最短路径(边数最少)。
在解决简单背包问题时,动态规划的状态转移方程如下:
dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1]);
该方程表示:在考虑第 个物品时,当前背包容量为 ,如果不放物品 ,则最大价值是 dp[i-1][w];如果放入物品 ,则最大价值是 dp[i-1][w - weights[i-1]] + values[i-1],其中数组 weights 和 values 分别表示所有物品的重量和值,数组下标从 开始。
A:正确。方程准确刻画 01 背包决策:不放第 i 件取 dp[i-1][w],放入则先腾出 weights[i-1] 容量再加 values[i-1],二者取最大值。
栈中元素的插入和删除操作都在栈的顶端进行,所以方便用双向链表比单向链表更合适实现。
B:错误。栈的操作只在栈顶一端进行,单向链表头插头删已是 O(1);双向链表多余的 prev 指针毫无用处,不能说更合适。