GESP认证C++编程真题解析 | 202509 五级

附上汇总帖:GESP认证C++编程真题解析 | 汇总


单选题

第1题

以下哪种情况使用链表比数组更合适?

A. 数据量固定且读多写少

B. 需要频繁在中间或开头插入、删除元素

C. 需要高效随机访问元素

D. 存储空间必须连续

【答案】:B

【解析】

链表在插入、删除操作时只需改变指针,效率高且不需要移动大量数据。数组在插入、删除时需移动元素,效率低。

第2题

函数 removeElements​ 删除单链表中所有结点值等于 val​ 的结点,并返回新的头结点,其中链表头结点为 head​ ,则横线处填写( )。

// 结点结构体
struct Node {
	int val;
	Node* next;

	Node() : val(0), next(nullptr) {}
	Node(int x) : val(x), next(nullptr) {}
	Node(int x, Node *next) : val(x), next(next) {}
};

Node* removeElements(Node* head, int val) {
	Node dummy(0, head); // 哑结点,统一处理头结点
	Node* cur = &dummy;
	while (cur->next) {
		if (cur->next->val == val) {
			_______________________ // 在此填入代码
		}
		else {
			cur = cur->next;
		}
	}
	return dummy.next;
}

A.

Node* del = cur;
cur = del->next;
delete del;

B.

Node* del = cur->next;
cur->next = del;
delete del;

C.

Node* del = cur->next;
cur->next = del->next;
delete del;

D.

Node* del = cur->next;
delete del;
cur->next = del->next;

【答案】:C

【解析】

要删除当前结点的下一个结点cur->next,必须先保存该结点指针del,再让cur->next指向del->next,最后释放del所占内存。选项C的顺序与逻辑正确。

第3题

函数 hasCycle​ 采用Floyd快慢指针法判断一个单链表中是否存在环,链表的头节点为 head​ ,即用两个指针 在链表上前进: slow​ 每次走 1 步,fast​ 每次走 2 步,若存在环,fast​ 终会追上 slow​ (相遇);若无环,fast​ 会先到达 nullptr,则横线上应填写( )。

struct Node {
	int val;
	Node *next;
	Node(int x) : val(x), next(nullptr) {}
};

bool hasCycle(Node *head) {
	if (!head || !head->next)
		return false;
	Node* slow = head;
	Node* fast = head->next;
	while (fast && fast->next) {
		if (slow == fast) return true;
		_______________________ // 在此填入代码
	}
	return false;
}

A.

slow = slow->next;
fast = fast->next->next;

B.

slow = fast->next;
fast = slow->next->next;

C.

slow = slow->next;
fast = slow->next->next;

D.

slow = fast->next;
fast = fast->next->next;

【答案】:A

【解析】

slow每次走一步,fast每次走两步。

第4题

函数 isPerfectNumber​ 判断一个正整数是否为完全数(该数是否即等于它的真因子之和),则横线上应填写( )。一个正整数 n​ 的真因子包括所有小于 n​ 的正因子,如28的真因子为1, 2, 4, 7, 14。

bool isPerfectNumber(int n) {
	if(n <= 1) return false;
	int sum = 1;
	for(int i = 2; ______; i++) {
		if(n % i == 0) {
			sum += i;
			if(i != n/i) sum += n/i;
		}
	}
	return sum == n;
}

A. i <= n​

B. i*i <= n​

C. i <= n/2​

D. i < n​

【答案】:B

【解析】

判断完全数需遍历真因子,利用因子成对出现的特性,只需遍历到 \(\sqrt n\) 即可。

第5题

以下代码计算两个正整数的最大公约数(GCD),横线上应填写( )。

int gcd0(int a, int b) {
	if (a < b) {
		swap(a, b);
	}
	while(b != 0) {
		int temp = a % b;
		a = b;
		b = temp;
	}
	return ______;
}

A. b​

B. a​

C. temp​

D. a * b​

【答案】:B

【解析】

循环结束时,变量 a 保存的就是最大公约数。

第6题

函数 sieve​ 实现埃拉托斯特尼筛法(埃氏筛),横线处应填入( )。

vector<bool> sieve(int n) {
	vector<bool> is_prime(n+1, true);
	is_prime[0] = is_prime[1] = false;
	for(int i = 2; i <= n; i++) {
		if(is_prime[i]) {
			for(int j = ______; j <= n; j += i) {
				is_prime[j] = false;
			}
		}
	}
	return is_prime;
}

A. i​

B. i+1​

C. i*2​

D. i*i​

【答案】:D

【解析】

埃氏筛的优化方式是从 \(i^2\) 开始标记,因为小于 \(i^2\) 的合数都已被更小的质数标记过。

第7题

函数 linearSieve​ 实现线性筛法(欧拉筛),横线处应填入( )。

vector<int> linearSieve(int n) {
	vector<bool> is_prime(n+1, true);
	vector<int> primes;
	for(int i = 2; i <= n; i++) {
		if(is_prime[i]) primes.push_back(i);
		for(int p : primes) {
			if(p * i > n) break;
			is_prime[p * i] = false;
			if(________) break;
		}
	}
	return primes;
}

A. i % p == 0​

B. p % i == 0​

C. i == p​

D. i * p == n​

【答案】:A

【解析】

线性筛通过"每个合数只被其最大质因子筛去"的策略,保证每个合数恰好被标记一次。

第8题

关于 埃氏筛 和 线性筛 的比较,下列说法错误的是( )。

A. 埃氏筛可能会对同一个合数进行多次标记

B. 线性筛的理论时间复杂度更优,所以线性筛的速度往往优于埃氏筛

C. 线性筛保证每个合数只被其最小质因子筛到一次

D. 对于常见范围(\(n\le 10^7\)),埃氏筛因实现简单,常数较小,其速度往往优于线性筛

【答案】:B

【解析】

虽然线性筛复杂度为 \(O(n)\),埃氏筛为 \(O(nloglogn)\),但二者在理论复杂度上差距极小,线性筛未必速度更快。

第9题

唯一分解定理描述的是( )。

A. 每个整数都能表示为任意素数的乘积

B. 每个大于 1 的整数能唯一分解为素数幂乘积(忽略顺序)

C. 合数不能分解为素数乘积

D. 素数只有两个因子:1 和自身

【答案】:B

【解析】

唯一分解定理指出,任一大于1的整数可唯一表示为素数幂的乘积(忽略顺序)。

第10题

给定一个 n x n​ 的矩阵 matrix​ ,矩阵的每一行和每一列都按升序排列。函数 countLE​ 返回矩阵中第 k​ 小的元素,则两处横线上应分别填写( )。

// 统计矩阵中 <= x 的元素个数:从左下角开始
int countLE(const vector<vector<int>>& matrix, int x) {
	int n = (int)matrix.size();
	int i = n - 1, j = 0, cnt = 0;
	while (i >= 0 && j < n) {
		if (matrix[i][j] <= x) {
			cnt += i + 1;
			++j;
		}
		else {
			--i;
		}
	}
	return cnt;
}

int kthSmallest(vector<vector<int>>& matrix, int k) {
	int n = (int)matrix.size();
	int lo = matrix[0][0];
	int hi = matrix[n - 1][n - 1];

	while (lo < hi) {
		int mid = lo + (hi - lo) / 2;
		if (countLE(matrix, mid) >= k) {
			________________ // 在此处填入代码
		} else {
			________________ // 在此处填入代码
		}
	}
	return lo;
}

A.

hi = mid - 1;
lo = mid + 1;

B.

hi = mid;
lo = mid;

C.

hi = mid;
lo = mid + 1;

D.

hi = mid + 1;
lo = mid;

【答案】:C

【解析】

当countLE(matrix, mid) >= k时,寿命答案 <=mid,应缩小右边界,否则应增加左边界。

第11题

下述C++代码实现了快速排序算法,下面说法错误的是( )。

int partition(vector<int>& arr, int low, int high) {
	int i = low, j = high;
	int pivot = arr[low]; // 以首元素为基准
	while (i < j) {
		while (i < j && arr[j] >= pivot) j--; //从右往左查找
		while (i < j && arr[i] <= pivot) i++; //从左往右查找
		if (i < j) swap(arr[i], arr[j]);
	}
	swap(arr[i], arr[low]);
	return i;
}

void quickSort(vector<int>& arr, int low, int high) {
	if (low >= high) return;
	int p = partition(arr, low, high);
	quickSort(arr, low, p - 1);
	quickSort(arr, p + 1, high);
}

A. 快速排序之所以叫“快速”,是因为它在平均情况下运行速度较快,常数小、就地排序,实践中通常比归并排序更高效。

B. 在平均情况下,划分的递归层数为 \(logn\),每层中的总循环数为,总时间为 \(O(nlogn)\)

C. 在最差情况下,每轮划分操作都将长度为 的数组划分为长度为 \(0\)\(n-1\) 的两个子数组,此时递归层数达到 \(n\),每层中的循环数为 \(n\),总时间为 \(O(n^2)\)

D. 划分函数 partition​ 中“从右往左查找”与“从左往右查找”的顺序可以交换。

【答案】:D

【解析】

partition 的查找顺序必须保持先移 j,再移 i,否则可能破坏基准值的正确位置划分。

第12题

下述C++代码实现了归并排序算法,则横线上应填写( )。

void merge(vector<int> &nums, int left, int mid, int right) {
	// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
	vector<int> tmp(right - left + 1);
	int i = left, j = mid + 1, k = 0;
	while (i <= mid && j <= right) {
		if (nums[i] <= nums[j])
			tmp[k++] = nums[i++];
		else
			tmp[k++] = nums[j++];
	}
	while (i <= mid) {
		tmp[k++] = nums[i++];
	}
	while (________) { // 在此处填入代码
		tmp[k++] = nums[j++];
	}
	for (k = 0; k < tmp.size(); k++) {
		nums[left + k] = tmp[k];
	}
}

void mergeSort(vector<int> &nums, int left, int right) {
	if (left >= right)
		return;

	int mid = (left + right) / 2;
	mergeSort(nums, left, mid);
	mergeSort(nums, mid + 1, right);
	merge(nums, left, mid, right);
}

A. i < mid​

B. j < right​

C. i <= mid​

D. j <= right​

【答案】:D

【解析】

最后阶段需处理右子数组剩余部分,因此条件是 j<=right,确保将剩余元素全部复制到临时数组 tmp 中。

第13题

假设你是一家电影院的排片经理,只有一个放映厅。你有一个电影列表 movies​ ,其中 movies[i] = [start_i, end_i]​ 表示第 i​ 部电影的开始和结束时间。请你找出最多能安排多少部不重叠的电影,则横线上应分别填写的代码为( )。

int maxMovies(vector<vector<int>>& movies) {
	if (movies.empty()) return 0;

	sort(movies.begin(), movies.end(), [](const vector<int>& a, const vector<int>& b) {
		return ______; // 在此处填入代码
	});

	int count = 1;
	int lastEnd = movies[0][1];

	for (int i = 1; i < movies.size(); i++) {
		if (movies[i][0] >= lastEnd) {
			count++;
			______ = movies[i][1]; // 在此处填入代码
		}
	}

	return count;
}

A. a[0] < b[0]lastEnd​

B. a[1] < b[1]lastEnd​

C. a[0] < b[0]movies[i][0]

D. a[1] < b[1]movies[i][0]

【答案】:B

【解析】

贪心策略是:按结束时间从小到大排序,依次选择起始时间不早于上一次结束时间的电影以获得更多场次。

第14题

给定一个整数数组 nums​ ,下面代码找到一个具有最大和的连续子数组,并返回该最大和。则下面说法错误的是( )。

int crossSum(vector<int>& nums, int left, int mid, int right) {
	int leftSum = INT_MIN, rightSum = INT_MIN;
	int sum = 0;
	for (int i = mid; i >= left; i--) {
		sum += nums[i];
		leftSum = max(leftSum, sum);
	}
	sum = 0;
	for (int i = mid + 1; i <= right; i++) {
		sum += nums[i];
		rightSum = max(rightSum, sum);
	}
	return leftSum + rightSum;
}

int helper(vector<int>& nums, int left, int right) {
	if (left == right)
		return nums[left];
	int mid = left + (right - left) / 2;
	int leftMax = helper(nums, left, mid);
	int rightMax = helper(nums, mid + 1, right);
	int crossMax = crossSum(nums, left, mid, right);
	return max({leftMax, rightMax, crossMax});
}

int maxSubArray(vector<int>& nums) {
	return helper(nums, 0, nums.size() - 1);
}

A. 上述代码采用分治算法实现

B. 上述代码采用贪心算法

C. 上述代码时间复杂度为 \(O(nlogn)\)

D. 上述代码采用递归方式实现

【答案】:B

【解析】

代码不是贪心算法。

第15题

给定一个由非负整数组成的数组 digits​ ,表示一个非负整数的各位数字,其中最高位在数组首位,且 digits​ 不含前导0(除非是0本身)。下面代码对该整数执行 +1 操作,并返回结果数组,则横线上应填写( )。

vector<int> plusOne(vector<int>& digits) {
	for (int i = (int)digits.size() - 1; i >= 0; --i) {
		if (digits[i] < 9) {
			digits[i] += 1;
			return digits;
		}
		________________ // 在此处填入代码
	}
	digits.insert(digits.begin(), 1);
	return digits;
}

A.

digits[i] = 0;

B.

digits[i] = 9;

C.

digits[i] = 1;

D.

digits[i] = 10;

【答案】:A

【解析】

模拟高精度加法的个位进位过程:当digits[i] == 9,加1后需进位,因此变为0,继续向高位进位。

判断题

第1题

基于下面定义的函数,通过判断 isDivisibleBy9(n) == isDigitSumDivisibleBy9(n)​ 代码可验算如果一个数能被9整除,则它的各位数字之和能被9整除。

bool isDivisibleBy9(int n) {
	return n % 9 == 0;
}

bool isDigitSumDivisibleBy9(int n) {
	int sum = 0;
	string numStr = to_string(n);
	for (char c : numStr) {
		sum += (c - '0');
	}
	return sum % 9 == 0;
}

A. 正确

B. 错误

【答案】:A

【解析】

isDigitSumDivisibleBy9 就是验证各位数字之和能被9整除。

第2题

假设函数 gcd()​ 能正确求两个正整数的最大公约数,则下面的 findMusicalPattern(4, 6)​ 函数返回2。

void findMusicalPattern(int rhythm1, int rhythm2) {
	int commonDivisor = gcd(rhythm1, rhythm2);
	int patternLength = (rhythm1 * rhythm2) / commonDivisor;
	return patternLength;
}

A. 正确

B. 错误

【答案】:B

【解析】

返回的是12,即最小公倍数。

第3题

下面递归实现的斐波那契数列的时间复杂度为 \(O(2^n)\)

long long fib_memo(int n, long long memo[]) {
	if (n <= 1) return n;
	if (memo[n] != -1) return memo[n];
	memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo);
	return memo[n];
}

int main() {
	int n = 40;
	long long memo[100];
	fill_n(memo, 100, -1);
	long long result2 = fib_memo(n, memo);
	return 0;
}

A. 正确

B. 错误

【答案】:B

【解析】

这是一个记忆化递归实现的斐波那契数列,时间复杂度为 \(O(n)\)

第4题

链表通过更改指针实现高效的结点插入与删除,但结点访问效率低、占用内存较多,且对缓存利用不友好。

A. 正确

B. 错误

【答案】:A

【解析】

链表随机访问时间复杂度为 \(O(n)\),每个结点除了存储数据,还需要存储指针,而且在内存中不连续。

第5题

二分查找依赖数据的有序性,通过循环逐步缩减一半搜索区间来进行查找,且仅适用于数组或基于数组实现的数据结构。

A. 正确

B. 错误

【答案】:A

【解析】

二分查找的核心是有序性和随机访问,并非只能用于数组。

第6题

线性筛关键是“每个合数只会被最小质因子筛到一次”,因此为 \(O(n)\)

A. 正确

B. 错误

【答案】:A

【解析】

关于线性筛的关键描述准确。

第7题

快速排序和归并排序都是稳定的排序算法。

A. 正确

B. 错误

【答案】:B

【解析】

快速排序不稳定,归并排序稳定。

第8题

下面代码采用分治算法求解标准 3 柱汉诺塔问题,时间复杂度为 \(O(nlogn)\)

void move(vector<int> &src, vector<int> &tar) {
	int pan = src.back();
	src.pop_back();
	tar.push_back(pan);
}

void dfs(int n, vector<int> &src, vector<int> &buf, vector<int> &tar) {
	if (n == 1) {
		move(src, tar);
		return;
	}

	dfs(n - 1, src, tar, buf);
	move(src, tar);
	dfs(n - 1, buf, src, tar);
}

void solveHanota(vector<int> &A, vector<int> &B, vector<int> &C) {
	int n = A.size();
	dfs(n, A, B, C);
}

A. 正确

B. 错误

【答案】:B

【解析】

这个分治算法的时间复杂度为 \(O(2^n)\)
\(T(n)=2⋅T(n−1)+1\),计算得到 \(T(n)=2^n-1\)

第9题

所有递归算法都可以转换为迭代算法。

A. 正确

B. 错误

【答案】:A

【解析】

递归和迭代是等价的计算模型,可以相互转换。

第10题

贪心算法总能得到全局最优解。

A. 正确

B. 错误

【答案】:B

【解析】

贪心并不总是能得到全局最优解,只有在问题具有贪心选择性质和最优子结构时才能得到全局最优解。

编程题

题解:洛谷 P14073 [GESP202509 五级] 数字选取

题解:洛谷 P14074 [GESP202509 五级] 有趣的数字和

posted @ 2026-02-15 16:33  团爸讲算法  阅读(77)  评论(0)    收藏  举报