GESP 客观题评测系统
2025-03-Level-5
2025-03-Level-5
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题)
链表不具备的特点是()。
正确答案A
解析详情
【答案】A
【考点】链表的基本特性
【解析】 链表是一种链式存储结构,访问元素需要从头节点开始逐个遍历,不支持随机访问。插入和删除只需修改指针,不需要移动元素。
【易错点】 混淆链表和数组的访问特性。
第 2 题(单选题)
双向链表中每个结点有两个指针域 prev 和 next,分别指向该结点的前驱及后继结点。设 p 指向链表中的一个结点,它的前驱结点和后继结点均非空。要删除结点 p,则下述语句中错误的是()。
p->next->prev = p->next;
p->prev->next = p->prev;
delete p;p->prev->next = p->next;
p->next->prev = p->prev;
delete p;p->next->prev = p->prev;
p->next->prev->next = p->next;
delete p;p->prev->next = p->next;
p->prev->next->prev = p->prev;
delete p;正确答案A
解析详情
【答案】A
【考点】双向链表的删除操作
【解析】 删除节点 p 时,应让 p 的前驱指向 p 的后继,p 的后继指向 p 的前驱。选项 A 中 p->next->prev = p->next 让后继的前驱指向了其自身,逻辑错误。
【易错点】 指针指向对象错误,导致链表断裂或形成自环。
第 3 题(单选题)
假设双向循环链表包含头尾哨兵结点(不存储实际内容),分别为 head 和 tail,链表中每个结点有两个指针域 prev 和 next,分别指向该结点的前驱及后继结点。下面代码实现了一个空的双向循环链表,横线上应填的最佳代码是()。
// 链表结点
template <typename T>
struct ListNode {
T data;
ListNode* prev;
ListNode* next;
// 构造函数
explicit ListNode(const T& val = T())
: data(val), prev(nullptr), next(nullptr) {
};
struct LinkedList {
ListNode<T>* head;
ListNode<T>* tail;
};
void InitLinkedList(LinkedList* list) {
list->head = new ListNode<T>;
list->tail = new ListNode<T>;
// 在此处填入代码
}
}list->head->prev = list->head;
list->tail->prev = list->head;list->head->next = list->tail;
list->tail->prev = list->head;list->head->next = list->tail;
list->tail->next = list->head;list->head->next = list->tail;
list->tail->next = nullptr;正确答案B
解析详情
【答案】B
【考点】双向链表的初始化
【解析】 在带有头尾哨兵的双向链表中,初始化空表时应建立 head 和 tail 的双向连接。即 head->next 指向 tail,且 tail->prev 指向 head。
【易错点】 忽略双向链表需要同时维护两个方向的指针。
第 4 题(单选题)
用以下辗转相除法(欧几里得算法)求gcd(84, 60)的步骤中,第二步计算的数是()。
int gcd(int a, int b) {
int big = a > b ? a : b;
int small = a < b ? a : b;
if (big % small == 0) {
return small;
}
return gcd(small, big % small);
}正确答案B
解析详情
【答案】B
【考点】辗转相除法(欧几里得算法)
【解析】 第一步计算 gcd(84, 60),因为 84 % 60 = 24,函数递归调用 gcd(60, 24)。因此第二步计算的两个数是 60 和 24。
【易错点】 混淆递归调用的参数顺序或步骤编号。
第 5 题(单选题)
根据唯一分解定理,下面整数的唯一分解是正确的()。
正确答案D
解析详情
【答案】D
【考点】唯一分解定理
【解析】 唯一分解定理要求将合数分解为若干个质因数的乘积。选项 A 中的 6、B 中的 4、C 中的 6 都不是质数。只有 D 项 30 = 2 × 3 × 5 且所有因数均为质数。
【易错点】 忽视了“唯一分解”必须分解到质数为止的要求。
第 6 题(单选题)
下述代码实现素数表的线性筛法,筛选出所有小于等于n的素数,横线上应填的最佳代码是()。
vector<int> sieve_linear(int n) {
vector<bool> is_prime(n + 1, true);
vector<int> primes;
if (n < 2) return primes;
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n / 2; i++) {
if (is_prime[i])
primes.push_back(i);
for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) { // 在此处填入代码
is_prime[i * primes[j]] = false;
if (i % primes[j] == 0)
break;
}
}
for (int i = n / 2 + 1; i <= n; i++) {
if (is_prime[i])
primes.push_back(i);
}
return primes;
}正确答案C
解析详情
【答案】C
【考点】线性筛(欧拉筛)实现
【解析】 在线性筛的内层循环中,必须保证访问 primes 数组不越界(j < primes.size()),同时要保证筛选的合数在目标范围内(i * primes[j] <= n)。选项 C 完整描述了这两个条件。
【易错点】 遗漏下标边界检查或筛选数值范围检查。
第 7 题(单选题)
在程序运行过程中,如果递归调用的层数过多,会因为()引发错误。
正确答案A
解析详情
【答案】A
【考点】递归与系统栈空间
【解析】 函数递归调用时,每一层调用都会在系统的调用栈(Stack)上分配存储空间。如果递归过深,消耗的空间超过了系统分配的限制,就会引发栈溢出错误。
【易错点】 混淆栈(Stack)和堆(Heap)的用途。
第 8 题(单选题)
对下面两个函数,说法错误的是()。
int factorialA(int n) {
if (n <= 1) return 1;
return n * factorialA(n-1);
}
int factorialB(int n) {
if (n <= 1) return 1;
int res = 1;
for (int i = 2; i <= n; i++)
res *= i;
}正确答案D
解析详情
【答案】D
【考点】递归与迭代的区别
【解析】 factorialA 通过调用函数自身实现,是典型的递归方式;factorialB 通过 for 循环实现,是典型的迭代方式。选项 D 的说法是错误的。
【易错点】 未能正确区分循环(迭代)和函数自调用(递归)。
第 9 题(单选题)
下算法中,()是不稳定的排序。
正确答案A
解析详情
【答案】A
【考点】排序算法稳定性
【解析】 在选择排序中,由于会发生长距离的元素交换,可能会改变相同元素的相对顺序,因此是不稳定的。冒泡、插入和归并排序通常实现为稳定的排序。
【易错点】 记错常见排序算法(冒泡、插入、选择、快速、归并等)的稳定性特征。
第 10 题(单选题)
考虑以下C++代码实现的快速排序算法,将数据从小到大排序,则横线上应填的最佳代码是()。
int partition(vector<int>& arr, int low, int high) {
int pivot = arr[high]; // 基准值
int i = low - 1;
for (int j = low; j < high; j++) {
// 在此处填入代码
}
swap(arr[i + 1], arr[high]);
return i + 1;
}
// 快速排序
void quickSort(vector<int>& arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}if (arr[j] > pivot) {
i++;
swap(arr[i], arr[j]);
}if (arr[j] < pivot) {
i++;
swap(arr[i], arr[j]);
}if (arr[j] < pivot) {
swap(arr[i], arr[j]);
i++;
}if (arr[j] == pivot) {
i++;
swap(arr[i], arr[j]);
}正确答案B
解析详情
【答案】B
【考点】快速排序的 Partition 实现
【解析】 在单向扫描的 Partition 算法中,当遇到小于基准值 pivot 的元素 arr[j] 时,应先移动较小元素区域的边界 i,然后交换 arr[i] 和 arr[j]。选项 B 符合该逻辑。
【易错点】 混淆指针移动和交换的顺序,或判断条件方向写反。
第 11 题(单选题)
若用二分法在内猜数,最多需要猜()次。
正确答案C
解析详情
【答案】C
【考点】二分查找的时间复杂度
【解析】 二分查找在长度为 n 的区间中查找,最坏情况下的比较次数为 ceil(log2(n))。对于 n=100,log2(100) 约等于 6.64,向上取整为 7 次。
【易错点】 误记对数底数或计算对数值时估值错误。
第 12 题(单选题)
下面代码实现了二分查找算法,在数组 arr 找到目标元素 target 的位置,则横线上能填写的最佳代码是()。
int binarySearch(int arr[], int left, int right, int target) {
while (left <= right) {
// 在此处填入代码
int mid = left + (right - left) / 2;
if (arr[mid] == target)
return mid;
else if (arr[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}正确答案A
解析详情
【答案】A
【考点】二分查找的实现细节
【解析】 在计算中间位置 mid 时,使用 left + (right - left) / 2 可以防止当 left 和 right 均很大时,直接相加导致的整数溢出问题。
【易错点】 忽略 (left + right) 在大数值下可能产生的溢出风险。
第 13 题(单选题)
贪心算法的核心特征是()。
正确答案A
解析详情
【答案】A
【考点】贪心算法概念
【解析】 贪心算法的基本特征是在解决问题的每一步中,都选择当前状态下的最优解,希望通过局部最优选择达到全局最优结果。
【易错点】 误认为贪心算法一定能找到全局最优解,或者将其与分治法混淆。
第 14 题(单选题)
函数 int findMax(int arr[], int low, int high) 计算数组中最大元素,其中数组 arr 从索引 low 到 high,()正确实现了分治逻辑。
if (low == high)
return arr[low];
int mid = (low + high) / 2;
return arr[mid];if (low >= high)
return arr[low];
int mid = (low + high) / 2;
int leftMax = findMax(arr, low, mid - 1);
int rightMax = findMax(arr, mid, high);
return leftMax + rightMax;if (low > high)
return 0;
int mid = low + (high - low) / 2;
int leftMax = findMax(arr, low, mid);
int rightMax = findMax(arr, mid + 1, high);
return leftMax * rightMax;if (low == high)
return arr[low];
int mid = low + (high - low) / 2;
int leftMax = findMax(arr, low, mid);
int rightMax = findMax(arr, mid + 1, high);
return (leftMax > rightMax) ? leftMax : rightMax;正确答案D
解析详情
【答案】D
【考点】分治算法应用
【解析】 分治法寻找最大值应将区间分为两半,分别递归求解左半部分和右半部分的最大值,最后通过比较得出两者中的较大者作为结果。选项 D 完整体现了分解、解决和合并的过程。
【易错点】 递归基准条件设置不当,或合并子问题结果的逻辑错误。
第 15 题(单选题)
小杨编写了一个如下的高精度乘法函数,则横线上应填写的代码为()。
vector<int> multiply(vector<int>& a, vector<int>& b) {
int m = a.size(), n = b.size();
vector<int> c(m + n, 0);
// 逐位相乘,逆序存储
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
c[i + j] += a[i] * b[j];
}
}
// 处理进位
int carry = 0;
for (int k = 0; k < c.size(); ++k) {
// 在此处填入代码
c[k] = temp % 10;
carry = temp / 10;
}
while (c.size() > 1 && c.back() == 0)
c.pop_back();
return c;
}正确答案B
解析详情
【答案】B
【考点】高精度算法进位处理
【解析】 在高精度进位处理中,当前位的最终数值 temp 应该是该位原有的累加值 c[k] 加上来自低位的进位 carry。因此填入 temp = c[k] + carry。
【易错点】 处理进位时忘记加上来自低位的进位值。
判断题(每题 2 分)
第 1 题(判断题)
要删除单链表中某个结点`p`(非尾结点),但不知道头结点,可行的操作是将`p->next`的数据拷贝到`p`的数据部分,将`p->next`设置为`p->next->next`,然后删除`p->next`。
正确答案正确
解析详情
【答案】正确
【考点】单链表节点的删除技巧
【解析】 在不知道头节点的情况下,无法直接通过前驱节点删除 p。通过将后继节点的数据复制给 p,并删除原后继节点,可以在逻辑上达到删除节点 p 的效果。
【易错点】 误认为删除链表节点必须持有前驱节点的指针。
第 2 题(判断题)
链表存储线性表时要求内存中可用存储单元地址是连续的。
正确答案错误
解析详情
【答案】错误
【考点】链表存储结构特点
【解析】 链表通过指针连接各个节点,在物理内存中可以存储在不连续的地址空间中,这是链表与数组(顺序存储)的核心区别。
【易错点】 混淆链表与数组在内存分配上的物理特性。
第 3 题(判断题)
线性筛相对于埃拉托斯特尼筛法,每个合数只会被它的最小质因数筛去一次,因此效率更高。
正确答案正确
解析详情
【答案】正确
【考点】素数筛选算法性能
【解析】 线性筛(欧拉筛)通过逻辑控制保证每个合数只被其最小质因数筛选一次,避免了埃氏筛中同一个数被多次重复筛选的问题,时间复杂度为线性的 O(n)。
【易错点】 不理解线性筛相比埃氏筛如何实现“每个合数只筛一次”。
第 4 题(判断题)
贪心算法通过每一步选择当前最优解,从而一定能获得全局最优解。
正确答案错误
解析详情
【答案】错误
【考点】贪心算法的局限性
【解析】 贪心算法只能保证每一步是局部最优的。虽然在某些特定问题上局部最优能导向全局最优,但对于许多问题,贪心策略并不能获得最终的最优解。
【易错点】 混淆局部最优选择与全局最优结果之间的关系。
第 5 题(判断题)
递归函数必须具有一个终止条件,以防止无限递归。
正确答案正确
解析详情
【答案】正确
【考点】递归的基本结构
【解析】 递归函数必须包含基准情况(终止条件),否则程序会陷入无限自调用,最终导致栈溢出(Stack Overflow)。
【易错点】 在编写递归程序时遗漏或错误设置边界退出条件。
第 6 题(判断题)
快速排序算法的时间复杂度与输入是否有序无关,始终稳定为。
正确答案错误
解析详情
【答案】错误
【考点】快速排序复杂度分析
【解析】 快速排序的性能高度依赖于基准值的选择。在最坏情况下(如输入已有序且选择边界作为基准),复杂度会退化到 O(n²)。只有平均情况和最好情况是 O(n log n)。
【易错点】 认为所有分治排序算法(如归并 and 快排)在各种情况下性能都保持恒定。
第 7 题(判断题)
归并排序算法的时间复杂度与输入是否有序无关,始终稳定为。
正确答案正确
解析详情
【答案】正确
【考点】归并排序复杂度稳定性
【解析】 归并排序通过严格的二分区间和线性合并,无论原始数组的有序程度如何,其比较和移动次数始终保持在 O(n log n) 级别。
【易错点】 未能区分归并排序与快速排序在最坏情况下的不同表现。
第 8 题(判断题)
二分查找适用于对无序数组和有序数组的查找。
正确答案错误
解析详情
【答案】错误
【考点】二分查找前提条件
【解析】 二分查找的核心逻辑是基于数据的有序性来排除一半的查找范围。如果数组是无序的,则无法判断目标值在中间位置的哪一侧,因此不适用。
【易错点】 认为只要是查找操作都可以应用高效的二分查找,忽略了数据预处理的要求。
第 9 题(判断题)
小杨有100元去超市买东西,每个商品有各自的价格,每种商品只能买1个,小杨的目标是买到最多数量的商品。小杨采用的策略是每次挑价格最低的商品买,这体现了分治思想。
正确答案错误
解析详情
【答案】错误
【考点】算法策略辨析
【解析】 “每次挑选当前价格最低的商品”属于典型的贪心策略(总是选择当前看起来最优的选项),而非分治思想。
【易错点】 未能准确识别问题场景对应的算法思想分类。
第 10 题(判断题)
归并排序算法体现了分治算法,每次将大的待排序数组分成大小大致相等的两个小数组,然后分别对两个小数组进行排序,最后对排好序的两个小数组合并成有序数组。
正确答案正确
解析详情
【答案】正确
【考点】分治算法在归并排序中的体现
【解析】 归并排序的过程严格遵循了分治法的三个步骤:分解(分为左右子数组)、解决(递归排序子数组)和合并(将有序子数组合并为整体有序数组)。
【易错点】 不理解分治思想在具体经典排序算法中的应用细节。