CCF编程能力等级认证GESP—C++5级—20250322
单选题(每题 2 分,共 30 分)
1、链表不具备的特点是( )。
A. 可随机访问任何一个元素
B. 插入、删除操作不需要移动元素
C. 无需事先估计存储空间大小
D. 所需存储空间与存储元素个数成正比
正确答案:A
考察知识点:链表的特性
解析:
选项A:链表需从头节点顺序访问(O(n)),不支持随机访问(数组通过索引O(1)访问),A正确。
其他选项:链表插入删除无需移动元素(B错误);动态分配空间(C错误);存储空间与元素个数成正比(含指针开销,D错误)。
2、双向链表中每个结点有两个指针域 prev 和 next ,分别指向该结点的前驱及后继结点。设 p 指向链表中的一个结点,它的前驱结点和后继结点均非空。要删除结点 p,则下述语句中错误的是( )。
A.
p->next->prev = p->next;
p->prev->next = p->prev;
delete p;
B.
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
C.
p->next->prev = p->prev;
p->next->prev->next = p->next;
delete p;
D.
p->prev->next = p->next;
p->prev->next->prev = p->prev;
delete p;
正确答案:A
考察知识点:双向链表的删除操作
解析:
正确步骤:删除节点p需将p的前驱节点的next指向p->next,p的后继节点的prev指向p->prev。
选项A错误:p->next->prev = p->next会导致p的后继节点的前驱指向自身,破坏链表结构。
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>;
________________________________ // 在此处填入代码
};
A.
list->head->prev = list->head;
list->tail->prev = list->head;
B.
list->head->next = list->tail;
list->tail->prev = list->head;
C.
list->head->next = list->tail;
list->tail->next = list->head;
D.
list->head->next = list->tail;
list->tail->next = nullptr;
正确答案:B
考察知识点:双向循环链表的初始化
解析:
哨兵节点作用:头哨兵head和尾哨兵tail简化边界判断,空链表时head->next = tail且tail->prev = head,形成循环。
选项B正确:初始化后链表为空,头哨兵指向尾哨兵,尾哨兵指回头哨兵。
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);
}
A. 84和60
B. 60和24
C. 24和12
D. 12和0
正确答案:B
考察知识点:辗转相除法的步骤
解析:
计算流程:
gcd(84,60) → 84%60=24 → 调用gcd(60,24)(第二步);
gcd(60,24) → 60%24=12 → 调用gcd(24,12);
返回12。
5、根据唯一分解定理,下面整数的唯一分解是正确的( )。
A. 18 = 3 × 6
B. 28 = 4 × 7
C. 36 = 2 × 3 × 6
D. 30 = 2 × 3 × 5
正确答案:D
考察知识点:唯一分解定理
解析:
唯一分解定理要求分解为素数乘积,选项D中30=2×3×5(均为素数)正确。其他选项含合数因子(如6=2×3)。
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++) { // 在此处填入代码
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;
}
A. j < primes.size()
B. i * primes[j] <= n
C. j < primes.size() && i * primes[j] <= n
D. j <= n
正确答案:C
考察知识点:线性筛法的循环条件
解析:
内层循环条件:需遍历已找到的素数(j < primes.size()),且i*primes[j] <=n(避免越界),选项C正确。
7、在程序运行过程中,如果递归调用的层数过多,会因为( )引发错误。
A. 系统分配的栈空间溢出
B. 系统分配的堆空间溢出
C. 系统分配的队列空间溢出
D. 系统分配的链表空间溢出
正确答案:A
考察知识点:递归的栈溢出
解析:
递归调用时系统为每层函数分配栈空间,层数过多会导致栈溢出,A正确。
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;
}
A. 两个函数的实现的功能相同。
B. 两个函数的时间复杂度均为O(n)。
C. factorialA采用递归方式。
D. factorialB采用递归方式。
正确答案:D
考察知识点:递归与迭代的实现
解析:
factorialA是递归,factorialB是迭代(for循环),D错误。
9、下算法中,( )是不稳定的排序。
A. 选择排序
B. 插入排序
C. 归并排序
D. 冒泡排序
正确答案:A
考察知识点:排序算法的稳定性
解析:
选择排序不稳定:例如[2,2,1],第一次选择1会与第一个2交换,破坏原有顺序。其他选项均为稳定排序。
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);
}
}
A.
if (arr[j] > pivot) {
i++;
swap(arr[i], arr[j]);
}
B.
if (arr[j] < pivot) {
i++;
swap(arr[i], arr[j]);
}
C.
if (arr[j] < pivot) {
swap(arr[i], arr[j]);
i++;
}
D.
if (arr[j] == pivot) {
i++;
swap(arr[i], arr[j]);
}
正确答案:B
考察知识点:快速排序的分区操作
解析:
分区逻辑:将小于基准值pivot的元素交换到左侧,i记录小于区边界。选项B正确:if (arr[j] < pivot) {i++; swap(arr[i], arr[j]);}。
11、若用二分法在[1, 100]内猜数,最多需要猜( )次。
A. 100
B. 10
C. 7
D. 5
正确答案:C
考察知识点:二分查找的最多次数
解析:
二分查找次数满足2^k ≥ 100,k=7(2⁷=128≥100),C正确。
12、下面代码实现了二分查找算法,在数组 arr 找到目标元素 target 的位置,则横线上能填写的最佳代码是( )。
int binarySearch(int arr[], int left, int right, int target) {
while (left <= right) {
________// 在此处填入代码
if (arr[mid] == target)
return mid;
else if (arr[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
A. int mid = left + (right - left) / 2;
B. int mid = left;
C. int mid = (left + right) / 2;
D. int mid = right;
正确答案:A
考察知识点:二分查找的中点计算
解析:
防溢出写法:mid = left + (right - left)/2等价于(left+right)/2,但避免left+right溢出,A正确。
13、贪心算法的核心特征是( )。
A. 总是选择当前最优解
B. 回溯尝试所有可能
C. 分阶段解决子问题
D. 总能找到最优解
正确答案:A
考察知识点:贪心算法的核心
解析:
贪心算法每次选择局部最优解,A正确。B是回溯,C是分治,D错误(贪心不一定全局最优)。
14、函数 int findMax(int arr[], int low, int high) 计算数组中最大元素,其中数组 arr 从索引low 到 high ,( )正确实现了分治逻辑。
A.
if (low == high)
return arr[low];
int mid = (low + high) / 2;
return arr[mid];
B.
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;
C.
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;
D.
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正确实现此逻辑。
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;
}
A. int temp = c[k];
B. int temp = c[k] + carry;
C. int temp = c[k] - carry;
D. int temp = c[k] * carry;
正确答案:B
考察知识点:高精度乘法的进位处理
解析:
进位计算:当前位值c[k]加上上一轮进位carry,再取模和商,选项B正确。
判断题(每题 2 分,共 20 分)
1、单链表中删除某个结点 p (非尾结点),但不知道头结点,可行的操作是将 p 的值设为 p->next 的值,然后
删除 p->next 。
正确答案:正确
解析:单链表删除非尾节点p,可复制p->next的值到p,再删除p->next,正确。
2、链表存储线性表时要求内存中可用存储单元地址是连续的。
正确答案:错误
解析:链表节点内存分散,通过指针连接,无需连续空间,错误。
3、线性筛相对于埃拉托斯特尼筛法,每个合数只会被它的最小质因数筛去一次,因此效率更高。
正确答案:正确
解析:线性筛每个合数仅被最小质因子标记,效率高于埃氏筛,正确。
4、贪心算法通过每一步选择当前最优解,从而一定能获得全局最优解。
正确答案:错误
解析:贪心算法可能得到局部最优而非全局最优(如找零钱问题),错误。
5、递归函数必须具有一个终止条件,以防止无限递归。
正确答案:正确
解析:递归需终止条件,否则无限递归导致栈溢出,正确。
6、快速排序算法的时间复杂度与输入是否有序无关,始终稳定为O(nlogn) 。
正确答案:错误
解析:快速排序在有序数组上时间复杂度为O(n²),错误。
7、归并排序算法的时间复杂度与输入是否有序无关,始终稳定为O(nlogn) 。
正确答案:正确
解析:归并排序时间复杂度始终为O(n log n),与输入无关,正确。
8、二分查找适用于对无序数组和有序数组的查找。
正确答案:错误
解析:二分查找仅适用于有序数组,错误。
9、小杨有100元去超市买东西,每个商品有各自的价格,每种商品只能买1个,小杨的目标是买到最多数量的商品。小杨采用的策略是每次挑价格最低的商品买,这体现了分治思想。
正确答案:错误
解析:策略为每次选价格最低商品,体现贪心思想,非分治,错误。
10、归并排序算法体现了分治算法,每次将大的待排序数组分成大小大致相等的两个小数组,然后分别对两个小数组进行排序,最后对排好序的两个小数组合并成有序数组。
正确答案:正确
解析:归并排序分治流程:二分→子问题排序→合并,正确。
编程题 (每题 25 分,共 50 分)
平均分配
【问题描述】
小 A 有2n件物品,小 B 和小 C 想从小 A 手上买走这些物品。对于第i件物品,小 B 会以
b
i
b_i
bi的价格购买,而小 C 会以
c
i
c_i
ci的价格购买。为了平均分配这2n件物品,小 A 决定小 B 和小 C 各自只能买走恰好n件物品。你能帮小 A 求出他卖出这2n件物品所能获得的最大收入吗?
【输入格式】
第一行,一个正整数n。
第二行,2n个整数
b
1
,
b
2
,
.
.
.
,
b
2
n
b_1, b_2, ..., b_{2n}
b1,b2,...,b2n。
第三行,2n个整数
c
1
,
c
2
,
.
.
.
,
c
2
n
c_1, c_2, ..., c_{2n}
c1,c2,...,c2n。
【输出格式】
一行,一个整数,表示答案。
【样例输入 1】
3
1 3 5 6 8 10
2 4 6 7 9 11
【样例输出 1】
36
【样例输入 2】
2
6 7 9 9
1 2 10 12
【样例输出 2】
35
【数据范围】
对于20%的测试点,保证
1
≤
n
≤
8
1 \le n \le 8
1≤n≤8。
对于另外20%的测试点,保证
0
≤
b
i
≤
1
,
0
≤
c
i
≤
1
0 \le b_i \le 1,0 \le c_i \le 1
0≤bi≤1,0≤ci≤1。
对于所有测试点,保证
1
≤
n
≤
1
0
5
,
o
≤
b
i
≤
1
0
9
,
0
≤
c
i
≤
1
0
9
1 \le n \le 10^5, o \le b_i \le 10^9, 0 \le c_i \le 10^9
1≤n≤105,o≤bi≤109,0≤ci≤109。
原根判断
【问题描述】
小 A 知道,对于质数p而言,p的原根g是满足以下条件的正整数:
- 1 < g < p 1 \lt g \lt p 1<g<p;
- g p − 1 g^{p-1} gp−1 mod p = 1;
- 对于任意 q ≤ i < p − 1 q \le i \lt p-1 q≤i<p−1均有 g i g^i gi mod p ≠ \neq = 1。
其中a mod p 表示 a 除以p的余数。
小A现在有一个整数a,请你帮他判断a是不是p的原根
【输入格式】
第一行,一个正整数T,表示测试数据组数。
每组测试数据包含一行,两个正整数a, p。
【输出格式】
对于每组测试数据,输出一行,如果a是p的原根则输出 Yes ,否则输出 No 。
【样例输入 1】
3
3 998244353
5 998244353
7 998244353
【样例输出 1】
Yes
Yes
No
【数据范围】
对于40%的测试点,保证
3
≤
p
≤
1
0
3
3 \le p \le 10^3
3≤p≤103。
对于所有测试点,保证
1
≤
T
≤
20
,
3
≤
p
≤
1
0
9
,
1
<
a
<
p
1 \le T \le 20,3 \le p \le 10^9,1 \lt a \lt p
1≤T≤20,3≤p≤109,1<a<p,p为质数。

1706

被折叠的 条评论
为什么被折叠?



