以下( )没有涉及C++语言的面向对象特性支持。
B:调用 printf 只是普通库函数调用,不涉及类、对象、继承等机制;构造 class/struct、调用类成员函数、由同一基类派生多个类都属于面向对象特性。
关于以下C++代码,( )行代码会引起编译错误。
01#include <iostream> 02using namespace std; 03 04class Base { 05private: 06 int a; 07protected: 08 int b; 09public: 10 int c; 11 Base() : a(1), b(2), c(3) {} 12}; 13 14class Derived : public Base { 15public: 16 void show() { 17 cout << a << endl; // Line 1 18 cout << b << endl; // Line 2 19 cout << c << endl; // Line 3 20 } 21};
A:Line 1。a 是 Base 的 private 成员,派生类 Derived 不能访问;b 是 protected、c 是 public,派生类均可访问,故只有 Line 1 编译报错。
有个元素,按照6,5,4,3,2,1的顺序进入栈S,下列( )的出栈序列是不能出现的( )。
C:模拟入栈 6,5,4,3 后弹出 3,再弹 4,此时栈顶是 5 而非 6,无法接着弹出 6,故 C 不可能;A、B、D 均可通过适当的入栈出栈时机实现。
采用如下代码实现检查输入的字符串括号是否匹配,横线上应填入的代码为( )。
01#include <iostream> 02#include <stack> 03#include <string> 04 05using namespace std; 06 07bool is_valid(string s) { 08 stack<char> st; 09 char top; 10 11 for (char& ch : s) { 12 if (ch == '(' || ch == '{' || ch == '[') { 13 st.push(ch); // 左括号入栈 14 } 15 else 16 { 17 if (st.empty()) 18 return false; 19 ____________ // 在此处填入代码 20 if ((ch == ')' && top != '(') || 21 (ch == '}' && top != '{') || 22 (ch == ']' && top != '[')) { 23 return false; 24 } 25 } 26 } 27 28 return st.empty(); // 栈为空则说明所有括号匹配成功 29}
A:遇到右括号先取栈顶 top=st.top() 再 st.pop() 弹出,随后与当前右括号配对比较;B 先 pop 再取 top 会取到下一个元素,C、D 用 front 对栈无效。
下面代码判断队列的第一个元素是否等于a,并删除该元素,横向上应填写( )。
01#include <iostream> 02#include <queue> 03using namespace std; 04 05bool is_front_equal(std::queue<int>& q, int a) { 06 bool is_equal = false; 07 if (!q.empty()) { 08 ____________ // 在此处填入代码 09 } 10 return is_equal; 11}
B:先判断队首是否等于 a(q.front()==a),再用 q.pop() 删除该元素;C 先删除再比较会拿不到原队首,D 中队列没有 top 操作。
假设字母表{a,b,c,d,e}在字符串出现的频率分别为10%,15%,30%,16%,29%。若使用哈夫曼编码方式对字母进行二进制编码,则字符abcde分别对应的一组哈夫曼编码的长度分别为( )。
编者注:官方原题此处写作
abcdef,但题目只给出了字母a至e及其五项频率;本题按题意修正为abcde。
B:3,3,2,2,2。频率 10,15,16,29,30 依次合并最小两个:10+15=25,25+16=41,29+30=59,41+59=100,得 a、b 深 3,c、d、e 深 2。
以下C++代码实现 位的格雷码,则横线上应填写( )。
01#include <iostream> 02#include <vector> 03#include <string> 04using namespace std; 05// 生成 n 位的格雷码 06vector<string> generate_graycode(int n) { 07 vector<string> graycode_list; 08 if (n <= 0) { 09 return graycode_list; 10 } 11 12 // 初始1位格雷码 13 graycode_list.push_back("0"); 14 graycode_list.push_back("1"); 15 16 // 迭代生成 n 位的格雷码 17 for (int i = 2; i <= n; i++) { 18 int current_size = graycode_list.size(); 19 20 for (int j = current_size - 1; j >= 0; j--) { 21 graycode_list.push_back("1" + graycode_list[j]); 22 } 23 24 for (int j = 0; j < current_size; j++) { 25 ____________ // 在此处填入代码 26 } 27 } 28 29 return graycode_list; 30}
B:对原序列前半段(下标 0 到 current_size-1)原地在每个编码前加前缀 0,即 graycode_list[j]=「0」+graycode_list[j];配合已倒序追加的加 1 部分,构成相邻仅一位不同的 n 位格雷码。
给定一棵二叉树,其前序遍历结果为:ABDECFG,中序遍历结果为:DEBACFG,则这棵树的正确后序遍历结果是( )。
A:先序首元素 A 是根,中序 DEB 在左、CFG 在右;左子树先序 BDE 中序 DEB 得 B(D(E)),右子树先序 CFG 中序 CFG 得 C(F(G));后序为 E D B G F C A。
一棵有 个结点的完全二叉树用数组进行存储与表示,已知根结点存储在数组的第个位置。若存储在数组第个位置的结点存在兄弟结点和两个子结点,则它的兄弟结点和右子结点的位置分别是( )。
C:完全二叉树数组存储中,i 的兄弟是 i±1、孩子为 2i 与 2i+1。位置 9 是位置 4 的左孩子,兄弟为 8,右子为 2×9+1=19。
二叉树的深度定义为从根结点到叶结点的最长路径上的结点数,则以下基于二叉树的深度优先搜索实现的深度计算函数中横线上应填写( )。
01// 定义二叉树的结点结构 02struct tree_node { 03 int val; 04 tree_node* left; 05 tree_node* right; 06 07 tree_node(int x) : val(x), left(nullptr), right(nullptr) {} 08}; 09 10// 计算二叉树的深度 11int max_depth(tree_node* root) { 12 if (root == nullptr) { 13 return 0; // 如果根结点为空,则深度为 0 14 } 15 16 int left_depth = max_depth(root->left); 17 int right_depth = max_depth(root->right); 18 19 ____________ // 在此处填入代码 20}
C:树深 = 左右子树深度较大者加 1(根自身),故 return max(left_depth, right_depth)+1;相加或只取 max 都漏算了根这一层。
二叉树的深度计算可以采用广度优先搜索来实现,以下基于 BFS 的深度计算函数中横线上应填写( )。
01#include <queue> 02 03int max_depth_bfs(tree_node* root) { 04 if (root == nullptr) { 05 return 0; // 如果树为空,深度为 0 06 } 07 08 queue <tree_node*> q; 09 q.push(root); 10 int depth = 0; 11 12 // 使用队列进行层序遍历 13 while (!q.empty()) { 14 ____________ // 在此处填入代码 15 for (int i = 0; i < level_size; ++i) { 16 tree_node* node = q.front(); 17 q.pop(); 18 19 if (node->left) { 20 q.push(node->left); 21 } 22 if (node->right) { 23 q.push(node->right); 24 } 25 } 26 } 27 28 return depth; 29}
A:每处理完一层深度加 1(depth++),并用 int level_size=q.size() 记录当前层结点数,供内层 for 只弹完本层结点。
二叉搜索树中的每个结点,其左子树的所有结点值都小于该结点值,右子树的所有结点值都大于该结点值。以下代码对给定的整数数组(假设数组中没有数值相等的元素),构造一个对应的二叉搜索树,横线上应填写( ):
01// 定义二叉树的结点结构 02struct tree_node { 03 int val; 04 tree_node* left; 05 tree_node* right; 06 07 tree_node(int x) : val(x), left(nullptr), right(nullptr) {} 08}; 09 10// 插入结点到二叉搜索树中 11tree_node* insert(tree_node* root, int val) { 12 if (root == nullptr) { 13 return new tree_node(val); 14 } 15 16 ____________ // 在此处填入代码 17 18 return root; 19} 20 21// 根据给定数组构造二叉搜索树 22tree_node* constructBST(const int arr[], int size) { 23 tree_node* root = nullptr; 24 25 for (int i = 0; i < size; ++i) { 26 root = insert(root, arr[i]); 27 } 28 return root; 29}
A:BST 插入时若 val<root->val 递归插入左子树并挂回 root->left,否则插入右子树;递归参数须是子树根(root->left/right),C、D 传 root 会死循环。
当输入数组为[5,3,7,2,4,6,8]时,按二叉搜索树的插入规则依次构建二叉树,并采用如下代码实现的遍历方式,得到的输出是( )。
01#include <iostream> 02using namespace std; 03 04// 遍历二叉搜索树,输出结点值 05void traversal(tree_node* root) { 06 if (root == nullptr) { 07 return; 08 } 09 10 traversal(root->left); 11 cout << root->val << " "; 12 traversal(root->right); 13}
B:traversal 是左根右的中序遍历,BST 中序遍历结果必为升序,故输出 2 3 4 5 6 7 8,与其他选项的层序/先序不同。
动态规划通常用于解决( )。
B:动态规划适合可分解为相互依赖子问题(子问题重叠、有最优子结构)的问题;无法分解的问题只能直接求解,贪心可解的未必需要 DP。
阅读以下用动态规划解决的-背包问题的函数,假设背包的容量 是,假设输入个物品的重量weights分别为1,3,4,6(单位为 kg),每个物品对应的价值values分别为20,30,50,60,则函数的输出为( )。
01#include <iostream> 02#include <vector> 03using namespace std; 04 05// 0/1背包问题 06int knapsack(int W, const vector<int>& weights, const vector<int>& values, int n) { 07 vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); 08 09 for (int i = 1; i <= n; ++i) { 10 for (int w = 0; w <= W; ++w) { 11 if (weights[i - 1] <= w) { 12 dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]); 13 } 14 else 15 { 16 dp[i][w] = dp[i - 1][w]; 17 } 18 } 19 } 20 21 return dp[n][W]; 22}
C:110。容量 10,取重量 4+6=10 得 50+60=110(或 1+3+6=10 得 110);其他组合如 1+3+4=8 只得 100,故最大价值 110。
C++、Python和JAVA等都是面向对象的编程语言。
A:正确。C++、Python、Java 都支持类、对象、封装、继承、多态等面向对象机制,同属面向对象编程语言,这与 C 语言面向过程不同。
在C++中,类的静态成员变量只能被该类对象的成员函数访问。
B:错误。静态成员变量属于类本身,公有静态成员在类外可用「类名::变量」直接访问,也可通过任意对象访问,并非只能被成员函数访问。
栈是一种线性结构,可通过数组或链表来实现。二者相比,数组实现占用的内存较少,链表实现的入队和出队操作的时间复杂度较低。
B:错误。数组实现与链表实现的入栈出栈都是 O(1),操作复杂度相同;数组可能预分配较多空间,链表每结点还多存指针,内存也未必更省。
运行以下C++代码,屏幕将输出derived class。
01#include <iostream> 02using namespace std; 03 04class base { 05public: 06 virtual void show() { 07 cout << "base class" << endl; 08 } 09}; 10 11class derived : public base { 12public: 13 void show() override { 14 cout << "derived class" << endl; 15 } 16}; 17 18int main() { 19 base* b; 20 derived d; 21 b = &d; 22 b->show(); 23 return 0; 24}
A:正确。show 是虚函数,b 指向 derived 对象 d 后调用 b->show(),按动态类型分派到 derived::show,输出 derived class。
如下列代码所示的基类(base)及其派生类(derived),则生成一个派生类的对象时,只调用派生类的构造函数。
01#include <iostream> 02using namespace std; 03 04class base { 05public: 06 base() { 07 cout << "base constructor" << endl; 08 } 09 ~base() { 10 cout << "base destructor" << endl; 11 } 12}; 13 14class derived : public base { 15public: 16 derived() { 17 cout << "derived constructor" << endl; 18 } 19 ~derived() { 20 cout << "derived destructor" << endl; 21 } 22};
B:错误。创建派生类对象时先调用基类构造函数再调用派生类构造函数,两者都会执行;析构时顺序相反,先派生类析构再基类析构。
哈夫曼编码本质上是一种贪心策略。
A:正确。哈夫曼算法每步贪心合并当前权值最小的两个结点,可证明局部最优的累计恰好得到全局最优的编码树,本质是贪心策略。,是贪心策略的典型应用。
如果根结点的深度记为,则一棵恰有个叶结点的二叉树的深度最少是。
A:正确。深度 d 的二叉树最多 2^(d-1) 个叶结点:深度 11 最多 1024 叶,不够 2024;深度 12 最多 2048 叶,满足,故最少深度为 12。
在非递归实现的树的广度优先搜索中,通常使用栈来辅助实现。
B:错误。广度优先搜索按层扩展、先入先出,非递归实现用队列辅助;栈是深度优先搜索(DFS)的辅助结构,二者不可互换。,二者不可互换。
状态转移方程是动态规划的核心,可以通过递推方式表示问题状态的变化。
A:正确。状态转移方程描述状态间如何递推转化,如 dp[i]=max(dp[i-1], nums[i]+dp[i-2]),是动态规划的核心。
应用动态规划算法时,识别并存储重叠子问题的解是必须的。
A:正确。重叠子问题使同一子问题被反复求解,DP 必须用数组等存储已算出的子问题解(记忆化),避免重复计算,这是 DP 高效的关键。