
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目描述
张老师组织了一场线上模拟考试,并要求考生使用学号登录网站进行考试。考试完成后张老师发现试卷提交不全,他需要找到哪些同学没有交卷。
若班级内有 N 个学生,则学号从 1 开始到 N 结束,每个学生一个学号。一个班级内学生个数不超过 80,即 0 < N ≤ 80 0 < N \le 80 0<N≤80。
请你编写一个程序,帮助张老师快速找到哪些学号的学生没有交卷。
二、输入描述
-
班级内学生数量。
-
交卷学生的学号列表,由于是按照交卷时间排序的,学号是乱序的,同时本题确保学号是合法的,不存在小于 1 或大于 N 的学号。
三、输出描述
- 未交卷学生的学号列表,按照学号升序排列。
四、测试用例
测试用例1:
1、输入
10
1,5,3,2,6,7,8,9,10
2、输出
4
3、说明
1、2、3、5、6、7、8、9、10 都已经交卷,所以只有 4 号未交卷。
测试用例2:
1、输入
10
9,2,3,5,6,7
2、输出
1,4,8,10
3、说明
已经交卷的是 2、3、5、6、7、9,因此未交卷的是 1、4、8、10。
五、解题思路
学生学号一定是 1 ~ N,而 N <= 80,非常适合使用一个布尔数组记录每个学生是否已经交卷。
例如 N = 10:
学号 1 2 3 4 5 6 7 8 9 10
是否交卷 √ √ √ × √ √ √ √ √ √
读取到一个已交卷学号 id 时:submitted[id] = true
最后从 1 扫描到 N,所有 submitted[i] == false 的学号就是未交卷学生。
这样还有一个好处:因为本身就是按照 1 ~ N 扫描,所以结果天然按照学号升序排列,完全不需要再排序。
六、Python算法源码
import sys
# 一次读取全部输入,并按照行进行拆分
lines = sys.stdin.read().splitlines()
if not lines:
sys.exit(0)
n = int(lines[0].strip())
# submitted[i] 表示学号 i 是否已经交卷
# 使用 n + 1 的长度,可以直接使用学号作为数组下标
submitted = [False] * (n + 1)
# 第二行可能为空,表示没有任何学生交卷
line = lines[1].strip() if len(lines) > 1 else ""
if line:
for item in line.split(","):
student_id = int(item.strip())
submitted[student_id] = True
/*
Python 中这里不能使用上面的注释形式,实际代码继续如下
*/
七、JavaScript算法源码
const fs = require('fs');
const input = fs.readFileSync(0, 'utf8').split(/\r?\n/);
const n = Number((input[0] || '').trim());
/*
* submitted[i] 表示学号 i 是否已经交卷。
* 长度设置为 n + 1,可以直接使用学号作为数组下标。
*/
const submitted = new Array(n + 1).fill(false);
// 第二行允许为空,空行表示没有任何学生交卷
const line = (input[1] || '').trim();
if (line.length > 0) {
const ids = line.split(',');
for (const item of ids) {
const studentId = Number(item.trim());
submitted[studentId] = true;
}
}
const result = [];
/*
* 从 1 到 n 顺序检查。
* 没有被标记的学生就是未交卷学生。
* 因为遍历顺序就是学号升序,所以无需额外排序。
*/
for (let i = 1; i <= n; i++) {
if (!submitted[i]) {
result.push(i);
}
}
console.log(result.join(','));
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int n;
if (scanf("%d", &n) != 1) {
return 0;
}
/*
* scanf 读取 N 后,输入缓冲区中还留有换行符。
* 这里将第一行剩余内容读取掉,
* 后面才能正确使用 fgets 读取第二行。
*/
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
}
/*
* submitted[i] 表示学号 i 是否已经交卷。
*
* calloc 会自动把内存初始化为 0:
* 0 -> 未交卷
* 1 -> 已交卷
*/
int *submitted =
(int *)calloc((size_t)n + 1, sizeof(int));
if (submitted == NULL) {
return 0;
}
char line[1024] = {0};
if (fgets(line, sizeof(line), stdin) != NULL) {
/*
* 使用 strtok 按逗号拆分学号。
* 如果第二行为空,则不会得到有效学号。
*/
char *token = strtok(line, ",\r\n");
while (token != NULL) {
int studentId = atoi(token);
submitted[studentId] = 1;
token = strtok(NULL, ",\r\n");
}
}
int first = 1;
/*
* 从 1 到 n 顺序扫描。
* 未被标记的学生就是未交卷学生。
* 顺序遍历可以直接保证结果按照学号升序。
*/
for (int i = 1; i <= n; i++) {
if (!submitted[i]) {
// 除第一个学号外,其余学号前面添加逗号
if (!first) {
printf(",");
}
printf("%d", i);
first = 0;
}
}
printf("\n");
free(submitted);
return 0;
}
九、C++算法源码
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
// 丢弃读取 N 后剩余的换行符
cin.ignore();
/*
* submitted[i] 表示学号 i 是否已经交卷。
* vector<bool> 只需要保存 true / false 状态,
* 非常适合本题。
*/
vector<bool> submitted(n + 1, false);
string line;
getline(cin, line);
if (!line.empty()) {
stringstream ss(line);
string token;
/*
* 按逗号拆分第二行。
* 每读取到一个学号,就在 submitted 中进行标记。
*/
while (getline(ss, token, ',')) {
int studentId = stoi(token);
submitted[studentId] = true;
}
}
bool first = true;
/*
* 从 1 到 n 顺序检查。
* false 表示这个学生没有出现在交卷名单中,
* 因此属于未交卷学生。
*
* 由于本身就是按照升序遍历,所以无需排序。
*/
for (int i = 1; i <= n; i++) {
if (!submitted[i]) {
if (!first) {
cout << ',';
}
cout << i;
first = false;
}
}
cout << '\n';
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

3075

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



