GESP 客观题评测系统

试卷解析 · 理解每一步

2026-09-Level-6

2026-09-Level-6

试卷解析总览,可直接查看每题答案与解析。

单选题

第 1 题(单选题,2 分)

下列代码执行后的输出结果是( )。

class Animal {
    public:
    virtual void speak() {
        cout << "Animal ";
    }
    virtual ~Animal() = default;
};

class Cat : public Animal {
    public:
    void speak() override {
        cout << "Cat ";
    }
};

int main() {
    Animal *p = new Cat();
    p->speak();
    delete p;
    return 0;
}
A.
Animal
B.
Cat
C.
Animal Cat
D.
编译错误

正确答案B

解析详情
答案 B虚函数与动态绑定
思路与推导
  1. p 的静态类型是 Animal*,实际指向 Cat 对象。speak 是虚函数,因此 p->speak() 调用 Cat 的重写版本,只输出 Cat。构造、析构函数没有其他输出,故选 B。

第 2 题(单选题,2 分)

下列代码中,横线处应填写( ),才能正确调用基类的带参数构造函数。

class Machine {
    protected:
    string id;

     public:
     Machine(string s) : id(s) {}
};

class Robot : public Machine {
    int level;

     public:
     Robot(string s, int n) : __________, level(n) {}
};
A.
Machine(s)
B.
Machine::id(s)
C.
super(s)
D.
id(s)

正确答案A

解析详情
答案 A派生类构造函数的初始化列表
思路与推导
  1. Robot 对象中包含 Machine 基类部分,必须先构造它。Machine 只有带 string 参数的构造函数,初始化列表应写 Machine(s),再初始化 level(n),故选 A。B 把成员当构造函数,C 不是 C++ 的基类调用语法;D 不能在派生类初始化列表中直接初始化继承成员。

第 3 题(单选题,2 分)

下列代码执行后的输出顺序是( )。

class Base {
    public:
    Base() {
        cout << "B ";
    }
    virtual ~Base() {
        cout << "~B ";
    }
};

class Derived : public Base {
    public:
    Derived() {
        cout << "D ";
    }
    ~Derived() {
        cout << "~D ";
    }
};

int main() {
    Base *p = new Derived();
    delete p;
    return 0;
}
A.
B D ~B ~D
B.
D B ~D ~B
C.
B D ~D ~B
D.
B D ~B

正确答案C

解析详情
答案 C构造与虚析构的执行顺序
思路与推导
  1. 创建 Derived 时,先构造 Base,输出 B,再执行 Derived 构造函数,输出 D。删除时基类析构函数是虚函数,先析构 Derived,输出 ~D,再析构 Base,输出 ~B。合起来为 B D ~D ~B,选 C。

第 4 题(单选题,2 分)

下列代码执行后的输出结果是( )。

stack<int> s;
queue<int> q;
for (int i = 2; i <= 6; i += 2) {
    s.push(i);
    q.push(i);
}
s.pop();
q.pop();
cout << s.top() << " " << q.front();
A.
2 4
B.
4 4
C.
4 6
D.
6 2

正确答案B

解析详情
答案 B栈与队列的删除顺序
思路与推导
  1. 依次加入 2、4、6 后,栈顶是 6,队首是 2。s.pop() 删除最后进入的 6,栈顶变为 4;q.pop() 删除最先进入的 2,队首也变为 4。因此输出 4 4,选 B。

第 5 题(单选题,2 分)

下面循环队列采用“空出一个位置”的方式区分队空和队满。横线处应填写( )。

const int MAXN = 8;
int data[MAXN];
int front = 0, rear = 0;

bool full() {
    return __________________________;
}
A.
rear == front
B.
(front + 1) % MAXN == rear
C.
(rear + 1) % MAXN == front
D.
rear == MAXN - 1

正确答案C

解析详情
答案 C循环队列的队满条件
思路与推导
  1. rear 指向下一次入队的位置,front 指向队首。预留一个空位置后,若 rear 再前进一步就到达 front,则不能继续入队,即 (rear + 1) % MAXN == front,选 C。rear == front 表示队空。

第 6 题(单选题,2 分)

下列函数实现了二叉树的哪种遍历方式( )。

void visit(TreeNode *root) {
    if (root == nullptr)
        return;
    visit(root->left);
    cout << root->val << " ";
    visit(root->right);
}
A.
前序遍历
B.
中序遍历
C.
后序遍历
D.
层序遍历

正确答案B

解析详情
答案 B二叉树的中序遍历
思路与推导
  1. 函数先递归访问左子树,再输出当前节点,最后递归访问右子树,顺序是左—根—右,因此属于中序遍历,选 B。前序为根—左—右,后序为左—右—根。

第 7 题(单选题,2 分)

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

A.
D E B F C A
B.
D B E F C A
C.
E D B F C A
D.
D E B C F A

正确答案A

解析详情
答案 A由先序和中序还原二叉树
思路与推导
  1. 先序首项 A 是根。中序中 A 左侧 D B E 构成左子树,右侧 C F 构成右子树。左子树根为 B,左右孩子分别是 D、E,后序为 D E B;右子树根为 C,右孩子为 F,后序为 F C。最后访问 A,得到 D E B F C A,选 A。

第 8 题(单选题,2 分)

下面函数用于计算二叉树的高度,横线处应填写( )。

int height(TreeNode *root) {
    if (root == nullptr)
        return 0;
    int leftH = height(root->left);
    int rightH = height(root->right);
    return __________________________;
}
A.
leftH + rightH
B.
min(leftH, rightH) + 1
C.
max(leftH, rightH)
D.
max(leftH, rightH) + 1

正确答案D

解析详情
答案 D二叉树高度的递归定义
思路与推导
  1. 空树高度为 0。非空树的最长向下路径经过左右子树中更高的一棵,再加当前根节点这一层,所以返回 max(leftH, rightH) + 1,选 D。叶子的左右高度都为 0,返回 1,符合定义。

第 9 题(单选题,2 分)

以下代码实现二叉树左子树优先的深度优先搜索算法,则横线上应填写( )。

void dfs(TreeNode *root) {
    if (root == nullptr)
        return;

     stack<TreeNode *> s;
     s.push(root);
     while (!s.empty()) {
         TreeNode *node = s.top();
         s.pop();
         cout << node->value << " ";

         ———————————————————————— // 在此处填入代码
     }
}
A.
s.push(node->right);
s.push(node->left);
B.
s.push(node->left);
s.push(node->right);
C.
if (node->right)
    s.push(node->right);
if (node->left)
    s.push(node->left);
D.
if (node->left)
    s.push(node->left);
if (node->right)
    s.push(node->right);

正确答案C

解析详情
答案 C用栈实现左子树优先 DFS
思路与推导
  1. 栈后进先出。要先访问左孩子,就必须先压入右孩子,再压入左孩子。同时应判断指针非空,避免后续弹出空指针后访问 node->value。两项要求只有 C 同时满足。

第 10 题(单选题,2 分)

下面函数在二叉搜索树中查找值 x 。横线处应填写( )。

TreeNode *searchBST(TreeNode *root, int x) {
    if (root == nullptr || root->val == x)
        return root;
    if (x < root->val)
        return searchBST(root->left, x);
    return __________________________;
}
A.
searchBST(root->left, x)
B.
searchBST(root->right, x)
C.
searchBST(root, x + 1)
D.
root->right

正确答案B

解析详情
答案 B二叉搜索树查找
思路与推导
  1. 前两个分支已经处理空树、当前值相等以及 x 小于当前值。执行到最后说明 x 大于 root->val,应在右子树继续递归查找,填写 searchBST(root->right, x),选 B。直接返回 root->right 不能保证该节点的值就是 x。

第 11 题(单选题,2 分)

有 55 个字符,其出现频率分别为 22、44、55、99、1212。按哈夫曼算法构造编码树,其最小带权路径长度 WPL 为( )。

A.
6868
B.
6969
C.
7171
D.
7373

正确答案B

解析详情
答案 B哈夫曼树的带权路径长度
思路与推导
  1. 每次合并两个最小权值:2+4=6,剩下 5、6、9、12;5+6=11;9+11=20;12+20=32。每次合并使该组叶子的深度增加 1,因此 WPL 等于所有合并权值之和,即 6+11+20+32=69,选 B。

第 12 题(单选题,2 分)

下面代码用反射法生成 n 位格雷编码,横线处应填写( )。

vector<string> gray(int n) {
    vector<string> ans = {"0", "1"};
    for (int bit = 2; bit <= n; ++bit) {
        int oldSize = ans.size();
        for (int i = oldSize - 1; i >= 0; --i)
            ans.push_back(__________________________);
        for (int i = 0; i < oldSize; ++i)
            ans[i] = "0" + ans[i];
    }
    return ans;
}
A.
ans[i] + "1"
B.
"0" + ans[i]
C.
"1" + ans[i]
D.
ans[oldSize - i - 1]

正确答案C

解析详情
答案 C格雷码反射生成法
思路与推导
  1. 先保留原序列,倒序遍历旧序列时给每项前面加 1,生成后半段;再给原来的前半段加 0。例如一位序列 0、1 变为 00、01、11、10。故填写 "1" + ans[i],选 C。倒序保证两段连接处只变最高位。

第 13 题(单选题,2 分)

下面代码计算走到第 n 级台阶的方法数,每次可以走 1 级或 2 级。横线处应填写( )。

int ways(int n) {
    if (n <= 2)
        return n;
    vector<int> dp(n + 1);
    dp[1] = 1;
    dp[2] = 2;
    for (int i = 3; i <= n; ++i)
        dp[i] = __________________________;
    return dp[n];
}
A.
dp[i - 1] + 1
B.
dp[i - 1] + dp[i - 2]
C.
dp[i - 2] + 2
D.
2 * dp[i - 1]

正确答案B

解析详情
答案 B台阶问题的动态规划转移
思路与推导
  1. 到第 i 级的最后一步只有两种:从 i-1 级走 1 级,或从 i-2 级走 2 级。两类走法不重叠且覆盖全部情况,所以 dp[i]=dp[i-1]+dp[i-2],选 B。例如 dp[3]=2+1=3。

第 14 题(单选题,2 分)

下面代码求从包含非负元素的数组中选择若干个互不相邻元素所能得到的最大和。横线处应填写( )。

int maxSum(vector<int> &a) {
    int n = a.size();
    if (n == 0)
        return 0;
    if (n == 1)
        return a[0];
    vector<int> dp(n);
    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.
dp[i - 2] + a[i]
C.
max(dp[i - 1], dp[i - 2] + a[i])
D.
max(dp[i - 1], a[i])

正确答案C

解析详情
答案 C不相邻元素最大和
思路与推导
  1. 考虑是否选择 a[i]。不选它时最优值为 dp[i-1];选择它就不能选择 a[i-1],最优值为 dp[i-2]+a[i]。取两者较大值,得到 max(dp[i-1], dp[i-2] + a[i]),选 C。

第 15 题(单选题,2 分)

下面是一维数组实现的 0/1 背包。内层循环必须从大到小枚举容量,主要原因是( )。

for (int i = 0; i < n; ++i) {
    for (int w = W; w >= weight[i]; --w) {
        dp[w] = max(dp[w], dp[w - weight[i]] + value[i]);
    }
}
A.
保证每件物品最多被选择一次
B.
保证物品必须按照重量从大到小选择
C.
降低时间复杂度到 O(n)O(n)
D.
防止数组 dp 发生越界

正确答案A

解析详情
答案 A0/1 背包一维数组的逆序更新
思路与推导
  1. 容量从大到小遍历时,较小容量 dp[w-weight[i]] 尚未被当前物品更新,仍代表只使用前面物品的结果。加上当前物品一次后就得到合法的 0/1 转移,因此选 A。若容量正序,可能读取已包含当前物品的状态,造成重复选择。

判断题

第 1 题(判断题,2 分)

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

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

     private:
     int age;
};

int main() {
    Student s;
}

正确答案错误

解析详情
答案 错误默认构造函数的生成条件
思路与推导
  1. 题目说法错误。类中已经声明 Student(int),编译器不会再隐式生成无参数构造函数。Student s; 需要调用无参构造函数,却找不到匹配定义,所以编译失败。可传入参数,或另行声明 Student()。

第 2 题(判断题,2 分)

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

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

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

正确答案错误

解析详情
答案 错误继承中的访问权限
思路与推导
  1. 题目说法错误。Base::value 是 private 成员,只能由 Base 自身的成员函数及友元直接访问。Child 虽继承 Base,也不能在 get() 中直接使用 value,所以代码编译失败。基类可提供访问函数,或在设计允许时使用 protected。

第 3 题(判断题,2 分)

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

queue<int> q;
q.push(10);
q.push(20);
q.push(30);
q.pop();
cout << q.front();

正确答案错误

解析详情
答案 错误队列的先进先出
思路与推导
  1. 依次入队后队列从前到后是 10、20、30。pop() 删除队首 10,剩下 20、30,front() 返回 20。题目声称输出 30,因此错误。

第 4 题(判断题,2 分)

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

正确答案正确

解析详情
答案 正确完全二叉树的顺序存储
思路与推导
  1. 从 1 开始按层编号时,节点 i 的左孩子编号是 2i,右孩子编号是 2i+1。例如根 1 的左右孩子为 2、3,节点 2 的左右孩子为 4、5。因此存在的左孩子位于 tree[2*i],说法正确。

第 5 题(判断题,2 分)

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

void inorder(TreeNode *root) {
    if (!root)
        return;
    inorder(root->left);
    cout << root->val << " ";
    inorder(root->right);
}

正确答案正确

解析详情
答案 正确二叉搜索树与中序有序性
思路与推导
  1. 二叉搜索树的左子树关键字不大于根,右子树关键字不小于根;各子树也满足同样性质。中序按左—根—右访问,因此整段序列非递减,说法正确。若所有关键字互异,则进一步是严格递增。

第 6 题(判断题,2 分)

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

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);
            }
        }
    }
}

正确答案正确

解析详情
答案 正确广度优先搜索与最少边数
思路与推导
  1. 队列使节点按距离逐层处理。起点距离为 0,从距离 d 的节点首次发现的邻点被记为 d+1,并立即标记,避免重复入队。更短路径上的前一层会更早被处理,因此首次到达的边数就是最少边数,判断正确。

第 7 题(判断题,2 分)

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

正确答案正确

解析详情
答案 正确哈夫曼编码的最优性与码长
思路与推导
  1. 哈夫曼算法每次合并两个最小频率的子树,得到最小带权路径长度。若较高频率字符反而具有更长码字,交换它和较低频率字符的位置就会减少总加权码长,与最优性矛盾。因此高频字符的码长不会更长,判断正确。

第 8 题(判断题,2 分)

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

正确答案错误

解析详情
答案 错误格雷码的相邻差一位性质
思路与推导
  1. 题目说法错误。格雷码保证相邻编码只差一位,并非任意两项都只差一位。例如二位格雷码 00、01、11、10 中,00 与 11 相差两位。这个反例已经否定“任意两个”。

第 9 题(判断题,2 分)

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

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]);
    }
}

正确答案正确

解析详情
答案 正确完全背包的容量正序更新
思路与推导
  1. 容量由小到大时,dp[w-weight[i]] 可能已经在本轮使用过第 i 种物品。再加 value[i] 就允许再选一件,所以同一种物品可使用多次,这是完全背包,判断正确。例如重量为 2 时,容量 4 可使用容量 2 的本轮结果。

第 10 题(判断题,2 分)

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

int fib(int n) {
    if (n <= 1)
        return n;
    return fib(n - 1) + fib(n - 2);
}

正确答案错误

解析详情
答案 错误朴素斐波那契递归的复杂度
思路与推导
  1. 题目说法错误。递归重复计算相同子问题,时间满足 T(n)=T(n-1)+T(n-2)+O(1),随 n 指数增长,不能记作 O(n)。但同时存在的调用栈最长只沿一条分支深入约 n 层,辅助空间是 O(n)。这里默认数值在 int 可表示范围内。