1. 题目解析与需求拆解这道题目来自华为OD机试真题考察的是对投票数据的统计和排序能力。题目要求我们处理一组选票数据最终输出得票最高的N位候选人。选票格式比较特殊每条记录包含一个推荐人和一个可选的反对方用逗号分隔。核心需求可以拆解为以下几个部分数据统计需要统计每个候选人获得的赞成票和反对票数量多级排序按照赞成票降序、反对票升序、姓名字典序的优先级排序结果输出输出前N位候选人的姓名用逗号连接这个题目看似简单但实际考察了多个编程基础能力字符串处理分割选票内容数据结构使用哈希表存储统计结果排序算法实现多条件比较边界条件处理空输入、单条记录等2. 数据结构设计与实现思路2.1 数据结构选择最核心的数据结构是用来存储候选人票数统计结果的容器。根据题目特点我们需要快速查找和更新候选人的票数能够遍历所有候选人进行排序Java实现中使用了HashMapString, int[]其中Key是候选人姓名Value是一个长度为2的数组[0]存储赞成票[1]存储反对票Go实现中使用了map[string]Score其中Score是一个自定义结构体type Score struct { pro, con int }这两种设计都能很好地满足需求区别在于Java版本使用原生数组内存更紧凑但可读性稍差Go版本使用结构体代码更清晰但会有轻微性能开销2.2 算法流程详解完整的处理流程可以分为四个步骤输入处理读取选票数量M逐行读取M张选票读取最终要输出的候选人数量N票数统计对每张选票按逗号分割第一部分总是赞成票第二部分可选反对票更新对应候选人的赞成/反对票数多级排序将统计结果转换为可排序的列表实现三级比较器赞成票降序反对票升序姓名字典序升序结果输出取排序后的前N个候选人用逗号连接姓名输出最终字符串3. Java实现深度解析3.1 核心代码解读Java版本使用了标准库中的集合类代码结构清晰// 使用 HashMap 存储票数统计 MapString, int[] scores new HashMap(); // 票数统计逻辑 scores.putIfAbsent(pro, new int[]{0, 0}); scores.get(pro)[0]; // 赞成票1 if (parts.length 1) { String con parts[1]; scores.putIfAbsent(con, new int[]{0, 0}); scores.get(con)[1]; // 反对票1 }这里有几个关键点putIfAbsent确保新候选人被正确初始化数组下标0和1分别代表赞成和反对票反对票是可选的需要检查数组长度3.2 多级排序实现Java使用Collections.sort配合自定义ComparatorCollections.sort(sortedCandidates, (a, b) - { // 第一优先级赞成票降序 if (a.getValue()[0] ! b.getValue()[0]) { return b.getValue()[0] - a.getValue()[0]; } // 第二优先级反对票升序 if (a.getValue()[1] ! b.getValue()[1]) { return a.getValue()[1] - b.getValue()[1]; } // 第三优先级姓名字典序 return a.getKey().compareTo(b.getKey()); });这种链式比较是处理多级排序的标准做法注意赞成票是降序所以用b-a反对票是升序所以用a-b字符串比较使用compareTo方法3.3 输出处理技巧Java使用了StringJoiner来优雅地构建输出字符串StringJoiner sj new StringJoiner(,); for (int i 0; i Math.min(n, sortedCandidates.size()); i) { sj.add(sortedCandidates.get(i).getKey()); } System.out.println(sj.toString());StringJoiner比手动拼接字符串更高效特别是自动处理逗号分隔避免最后一个元素的多余逗号线程安全4. Go实现深度解析4.1 数据结构设计Go版本定义了专门的结构体来存储票数type Score struct { pro, con int } type Candidate struct { name string score Score }这种设计比使用原生map更符合Go的惯用法类型安全避免魔法数字代码自文档化易于扩展新字段4.2 排序实现差异Go使用sort.Slice配合自定义比较函数sort.Slice(candidates, func(i, j int) bool { a, b : candidates[i], candidates[j] if a.score.pro ! b.score.pro { return a.score.pro b.score.pro // 赞成票降序 } if a.score.con ! b.score.con { return a.score.con b.score.con // 反对票升序 } return a.name b.name // 姓名字典序 })与Java的主要区别Go的比较函数返回bool而不是int需要手动解引用结构体字段排序是原地(in-place)操作4.3 Go特有的输入处理Go使用了bufio.Scanner处理输入scanner : bufio.NewScanner(os.Stdin) scanner.Scan() m, _ : strconv.Atoi(strings.TrimSpace(scanner.Text()))需要注意的细节每次Scan()读取一行需要手动处理字符串转换和trim错误处理被简化了实际生产代码应该检查错误5. 性能分析与优化思路5.1 时间复杂度分析算法的主要时间消耗在票数统计O(M)M是选票数量排序O(K log K)K是候选人数量由于K最多是2M每人最多出现在一张票的赞成或反对位所以整体复杂度是O(M log M)。5.2 空间复杂度分析需要存储票数统计O(K)空间排序列表O(K)空间因此空间复杂度是O(M)与输入规模线性相关。5.3 可能的优化方向内存优化Java中可以使用int[2]替代Integer数组减少内存开销Go中可以考虑使用指针减少结构体复制并行处理票数统计阶段可以分片并行处理适合大规模数据(M10000)的情况输入优化对于超大数据可以使用流式处理避免一次性加载所有选票到内存6. 边界条件与异常处理6.1 常见边界情况空输入M0时应该输出空字符串需要检查scanner.hasNextInt()单条记录只有一张选票时直接返回推荐人N1时不需要逗号分隔全等票数所有候选人票数相同时按名字排序测试用例2就是这种情况6.2 防御性编程建议输入验证if (!scanner.hasNextInt()) return; int m scanner.nextInt(); if (m 0) return;名字格式检查确保名字只包含小写字母处理前后空格N值校验n min(n, len(candidates)) // 避免越界7. 测试用例设计7.1 基础测试用例正常情况3 alice,bob bob,alice charlie 2预期输出bob,alice全等票数2 alice,bob bob,alice 1预期输出alice按字典序7.2 边界测试用例最小输入1 alice 1预期输出alice最大输入500 a1,a2 a2,a3 ... a500,a1 250验证性能和正确性7.3 异常测试用例空选票3 alice,bob charlie 1应该跳过空行非名字1 ALICE,bob 1根据题目说明应该不会出现8. 实际开发中的经验分享8.1 调试技巧中间输出System.err.println(Processing vote: line);统计结果检查fmt.Fprintf(os.Stderr, Stats: %v\n, scores)排序前数据 打印出排序前的完整列表验证统计是否正确8.2 常见陷阱换行符处理 Java中nextInt()后需要用nextLine()消耗换行符反对票初始化 反对票候选人可能之前没有出现过需要先初始化N值边界 N可能大于实际候选人数量需要取最小值8.3 代码风格建议Java版本使用record替代Map.EntryJava 14考虑使用var简化局部变量声明Go版本为Candidate实现String()方法方便调试使用strings.Builder替代[]string拼接9. 扩展思考与变种题目9.1 可能的变种多级选举 每个候选人也有投票权形成层级关系权重投票 不同选票有不同的权重值动态选举 支持实时添加新选票并查询当前结果9.2 实际应用场景员工评优系统 类似题目中的明日之星选举产品特性投票 用户投票决定产品开发优先级竞赛评分系统 综合多个评委的评分和反对意见10. 语言特性对比10.1 Java vs Go实现差异特性Java实现Go实现数据结构HashMapString, int[]map[string]Score排序方式Collections.sortsort.Slice输入处理Scannerbufio.Scanner字符串拼接StringJoinerstrings.Join类型安全一般使用数组好使用结构体10.2 性能考虑小数据量Go版本通常启动更快Java版本可能优化更好大数据量Java的HashMap经过高度优化Go的map在并发场景有优势内存使用Go版本通常更节省内存Java版本可以调优JVM参数11. 总结与个人体会通过实现这个题目我有以下几点深刻体会问题分析比编码更重要花时间彻底理解题目要求可以避免后期大量返工。我最初忽略了反对票可能不存在的情况导致测试不通过。多级排序是常见考点这种优先级的比较逻辑在很多实际场景都会遇到比如电商的商品排序、社交媒体的内容排序等。边界条件决定代码质量处理好了各种边界情况空输入、全等票数等才能写出健壮的代码。语言特性影响实现方式Java和Go虽然都能解决问题但惯用实现方式有很大不同了解这些差异有助于写出更地道的代码。在实际开发中这类数据处理任务非常常见。掌握好基础的数据结构和算法能够根据需求选择最合适的实现方式是程序员的核心能力之一。