找出所有成绩恰好在中位数水平的员工

61 阅读4分钟

找出所有成绩恰好在中位数水平的员工

背景介绍

某公司举办了一场全员技能竞赛,现在需要对成绩进行分析。其中一项重要的分析指标是找出所有成绩恰好在中位数水平的员工,以便进一步了解中间梯队的人员情况。

核心概念

  1. 员工记录 (Employee Record):

    • 每一条记录包含员工的 name (姓名) 和 score (分数)。
    • 姓名是唯一的。
  2. 中位数定义 (Median Definition):

    • 这是本题的一个特殊规则,请务必注意。

    • 首先,将所有员工的分数收集起来并按从小到大的顺序排列。

    • 然后,根据分数的总个数来确定中位数:

      • 如果分数总个数是奇数: 中位数就是排序后位于最中间的那个分数。
      • 如果分数总个数是偶数: 中位数是排序后位于中间的两个分数中较小的那个。

任务要求

  1. 根据所有员工的分数,计算出中位数分数
  2. 找出所有分数等于这个中位数分数的员工。
  3. 将这些员工的姓名按其在原始输入中的顺序倒序排列。
  4. 返回最终的姓名列表。

解答要求

  • 时间限制: 1000ms
  • 内存限制: 256MB

输入格式

  • employeeList: 一个员工记录列表。

    • 1 <= employeeList.length <= 100
    • employeeList[i].name: 字符串,仅由英文字母和数字组成,长度 [1, 10],姓名不重复。
    • employeeList[i].score: 整数,0 <= score <= 100

输出格式

  • 一个由分数等于中位数的员工姓名组成的字符串,姓名之间用单个空格分隔。

样例

输入样例 1

[["A02", 34], ["Li01", 32], ["B03", 34], ["C04", 56], ["A05", 79]]

输出样例 1

B03 A02

样例 1 解释

  1. 步骤 1 & 2: 提取并排序所有分数

    • 原始分数列表: [34, 32, 34, 56, 79]
    • 排序后: [32, 34, 34, 56, 79]
  2. 步骤 3: 计算中位数

    • 共有 5 个分数(奇数个)。
    • 排序后最中间(第 3 个)的数是 34。所以中位数为 34。
  3. 步骤 4: 查找匹配员工

    • 遍历原始输入列表,找到所有分数为 34 的员工。他们是:

      • "A02" (在输入的第 0 个位置)
      • "B03" (在输入的第 2 个位置)
  4. 步骤 5: 按规则排序结果

    • 题目要求按输入顺序的逆序输出。
    • 在输入中,"B03" 的出现位置比 "A02" 晚。
    • 因此,逆序排列后,"B03" 在前,"A02" 在后。
  5. 最终输出: "B03 A02"


输入样例 2

[["01", 10], ["1b", 10], ["2a", 9], ["02", 9]]

输出样例 2

02 2a

样例 2 解释

  1. 排序分数: [9, 9, 10, 10]
  2. 计算中位数: 共有 4 个分数(偶数个)。中间两个数是 910。根据规则,取较小的那个,所以中位数为 9
  3. 查找匹配员工: 分数为 9 的员工是 "2a""02"
  4. 排序结果: 在输入中,"02""2a" 之后。按输入顺序逆序,"02" 在前。
  5. 最终输出: "02 2a"
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 解决“中位数成绩”问题的实现类.
 */
public class MedianScoreFinder {
    /**
     * 内部静态类,用于表示一名员工.
     * 封装了员工的姓名、分数以及其在原始输入数组中的索引。
     * 记录原始索引是实现“按输入顺序逆序输出”的关键。
     */
    private static class Employee {
        String name;
        int score;
        int originalIndex; // 员工在输入列表中的原始位置

        public Employee(String name, int score, int originalIndex) {
            this.name = name;
            this.score = score;
            this.originalIndex = originalIndex;
        }
    }

    /**
     * 主方法,计算分数中位数,并返回分数等于中位数的员工姓名列表.
     * @param employeeData 输入的员工数据,格式为 [[name, score], ...]
     * @return 按输入顺序逆序排列的、分数等于中位数的员工姓名列表
     */
    public List<String> findMedianEmployees(Object[][] employeeData) {

        // --- 步骤 1: 解析输入,并创建员工对象列表 ---
        List<Employee> employees = new ArrayList<>();
        List<Integer> scores = new ArrayList<>();
        for (int i = 0; i < employeeData.length; i++) {
            String name = (String) employeeData[i][0];
            int score = (Integer) employeeData[i][1];
            
            employees.add(new Employee(name, score, i));
            scores.add(score);
        }

        // --- 步骤 2: 对分数列表进行排序,以计算中位数 ---
        Collections.sort(scores);

        // --- 步骤 3: 根据题目定义计算中位数 ---
        int medianScore;
        int n = scores.size();
        if (n % 2 != 0) {
            // 如果员工数量是奇数,中位数是排序后最中间的那个数
            medianScore = scores.get(n / 2);
        } else {
            // 如果员工数量是偶数,中位数是中间两个数中较小的那个
            // 中间两个数的索引是 (n/2 - 1) 和 (n/2)
            medianScore = scores.get(n / 2 - 1);
        }

        // --- 步骤 4: 筛选出所有分数等于中位数的员工 ---
        List<Employee> medianEmployees = new ArrayList<>();
        for (Employee emp : employees) {
            if (emp.score == medianScore) {
                medianEmployees.add(emp);
            }
        }

        // --- 步骤 5: 按输入顺序的逆序对结果进行排序并提取姓名 ---
        // 通过比较员工的 originalIndex 进行降序排序,即可实现“按输入顺序逆序”
        medianEmployees.sort(Comparator.comparingInt((Employee emp) -> emp.originalIndex).reversed());

        // 使用 Stream API 将排好序的员工对象列表映射为其姓名列表
        return medianEmployees.stream()
                .map(emp -> emp.name)
                .collect(Collectors.toList());
    }
}