华为OD机试真题中的“选修课”题目,主要考察的是对数据的处理能力和排序算法的理解。以下是对该题目的详细解析:
一、题目描述
现有两门选修课,每门选修课都有一部分学生选修,每个学生都有选修课的成绩。要求找出同时选修了两门选修课的学生,并按照以下规则进行排序和输出:
- 先按照班级进行划分,班级编号小的先输出。
- 每个班级内,按照两门选修课成绩和的降序排序。
- 成绩相同时,按照学生的学号升序排序。
二、输入输出
输入:
- 第一行为第一门选修课学生的成绩,
- 第二行为第二门选修课学生的成绩,每行数据中学生之间以英文分号分隔,每个学生的学号和成绩以英文逗号分隔,
- 学生学号的格式为 8 位数字(2 位院系编号+入学年份后2 位+院系内部1位专业编号+所在班级3位学号),
- 学生成绩的取值范围为「8,100]之间的整数,
- 两门选修课选修学生数的取值范围为[1-2000]之间的整数。
输出:
- 同时选修了两门选修课的学生的学号,如果没有同时选修两门选修课的学生输出NULL否则,
- 先按照班级划分,班级编号小的先输出,
- 每个班级先输出班级编号(学号前五位),然后另起一行输出这个班级同时选修两门选修课的学生学号,
- 学号按照要求排序(按照两门选修课成绩和的降序,
- 成绩和相同时按照学号升序学生之间以英文分号分隔。
三、解题思路
- 数据读取与存储:首先读取输入的两行数据,分别存储到两个列表中。每个列表中的元素是一个包含班级编号、学号和成绩的字符串,可以通过分割字符串来获取这些信息。
- 找出同时选修两门课程的学生:遍历两个列表,通过比较班级编号和学号来找出同时选修了两门课程的学生。可以将这些信息存储到一个字典中,字典的键是班级编号+学号,值是两门课程的成绩。
-
排序:根据题目要求,先按照班级编号排序,然后按照两门课成绩和的降序排序,如果成绩和相同,则按照学号升序排序。可以使用Java的
sorted
函数和自定义排序规则来实现。 - 输出:按照排序后的顺序输出学生的班级编号和学号。
四、代码参考
以下是一个可能的代码实现:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.stream.Collectors;
class Student {
String studentId; // 学号
String classId; // 班级编号(学号前五位)
int score1; // 第一门课成绩
int score2; // 第二门课成绩
public Student(String studentId, String classId, int score1, int score2) {
this.studentId = studentId;
this.classId = classId;
this.score1 = score1;
this.score2 = score2;
}
// 用于排序的比较器
public static Comparator<Student> getComparator() {
return Comparator.comparing(Student::getClassId)
.thenComparingInt(s -> -(s.score1 + s.score2)) // 降序,所以取负值
.thenComparing(Student::getStudentId);
}
// Getter 方法
public String getStudentId() {
return studentId;
}
public String getClassId() {
return classId;
}
}
public class ElectiveCourses {
public static void main(String[] args) {
// 创建Scanner对象读取输入
Scanner scanner = new Scanner(System.in);
// 读取第一行输入,并分割成数组
String[] input1 = scanner.nextLine().split(";");
// 读取第二行输入,并分割成数组
String[] input2 = scanner.nextLine().split(";");
// 创建存储学生信息的Map,键为学号,值为Student对象
Map<String, Student> students = new HashMap<>();
// 处理第一行输入,初始化学生信息
for (String item : input1) {
String[] parts = item.split(",");
String studentId = parts[0];
String classId = studentId.substring(0, 5); // 提取班级编号
int score1 = Integer.parseInt(parts[1]);
students.put(studentId, new Student(studentId, classId, score1, 0));
}
// 处理第二行输入,更新学生的第二门课成绩
for (String item : input2) {
String[] parts = item.split(",");
String studentId = parts[0];
int score2 = Integer.parseInt(parts[1]);
if (students.containsKey(studentId)) {
students.get(studentId).score2 = score2;
}
}
// 筛选出同时选修了两门课的学生
List<Student> enrolledStudents = students.values().stream()
.filter(s -> s.score2 != 0) // 假设初始化为0表示未选修第二门课
.collect(Collectors.toList());
// 如果没有同时选修两门选修课的学生,输出NULL并结束程序
if (enrolledStudents.isEmpty()) {
System.out.println("NULL");
scanner.close();
return;
}
// 对学生进行排序
enrolledStudents.sort(Student.getComparator());
// 输出结果
Map<String, List<String>> resultMap = new TreeMap<>();
for (Student student : enrolledStudents) {
resultMap.computeIfAbsent(student.getClassId(), k -> new ArrayList<>()).add(student.getStudentId());
}
// 遍历结果Map,输出班级编号和对应的学生学号
for (Map.Entry<String, List<String>> entry : resultMap.entrySet()) {
System.out.println(entry.getKey());
String studentIds = String.join(";", entry.getValue());
System.out.println(studentIds);
}
// 关闭Scanner
scanner.close();
}
}
注意:上述代码是一个简化的示例,用于说明解题思路。在实际应用中,可能需要根据具体的输入格式和要求进行适当的调整。例如,如果输入数据中的班级编号和学号不是固定的长度,或者存在其他特殊情况,需要相应地修改代码来处理这些情况。
五、运行解析
输入示例
01234001,90;01234002,85;05678001,95;01234003,88
01234001,92;01234003,89;05679001,78
解析步骤
1、读取输入:
- 第一行输入:01234001,90;01234002,85;05678001,95;01234003,88
- 第二行输入:01234001,92;01234003,89;05679001,78
2、处理第一行输入:
- 01234001,90 -> 学号: 01234001, 班级编号: 01234, 成绩1: 90
- 01234002,85 -> 学号: 01234002, 班级编号: 01234, 成绩1: 85
- 05678001,95 -> 学号: 05678001, 班级编号: 05678, 成绩1: 95
- 01234003,88 -> 学号: 01234003, 班级编号: 01234, 成绩1: 88
3、处理第二行输入:
- 01234001,92 -> 更新学号: 01234001 的成绩2为 92
- 01234003,89 -> 更新学号: 01234003 的成绩2为 89
- 05679001,78 -> 学号: 05679001 没有在第一行输入中出现,因此不会被处理
4、筛选同时选修两门课程的学生:
- 01234001 -> 成绩1: 90, 成绩2: 92
- 01234003 -> 成绩1: 88, 成绩2: 89
5、排序:
- 根据班级编号、总成绩(降序)和学号排序:
- 01234001 -> 班级编号: 01234, 总成绩: 182
- 01234003 -> 班级编号: 01234, 总成绩: 177
6、输出结果:
- 班级编号: 01234
- 学号: 01234001;01234003
输出结果
01234
01234001;01234003
懒得在控制台输入的话:
// 模拟输入数据
String inputData = "01234001,90;01234002,85;05678001,95;01234003,88\n" +
"01234001,92;01234003,89;05679001,78";
// 使用ByteArrayInputStream来模拟标准输入
ByteArrayInputStream in = new ByteArrayInputStream(inputData.getBytes());
System.setIn(in);
Scanner scanner = new Scanner(System.in);
总的来说,这道题目主要考察了数据处理和排序算法的应用,需要细心地处理输入数据,并按照题目要求进行排序和输出。