资讯动态

华为机试模拟题8详解:多条件排序的实战实现与避坑指南

发布时间:2026/8/29 7:42:51 来源:尧图企业网站定制
1. 模拟题8题目精读与考点定位1.1 题目完整描述先说题目本身。这套华为机试编程模拟题8里比较有代表性的一道题是任务执行顺序排序完整描述如下某嵌入式系统中有N个待执行任务每个任务包含任务ID整数、执行时长整数单位毫秒、优先级整数取值范围1~10数字越大优先级越高。现在需要按以下规则对所有任务进行排序优先级高的任务排在前面若优先级相同则执行时长短的任务排在前面若执行时长也相同则任务ID小的排在前面。输入第一行为一个整数T1 ≤ T ≤ 10表示测试数据组数。每组测试数据第一行为一个整数N1 ≤ N ≤ 1000接下来N行每行包含三个整数任务ID、执行时长、优先级以空格分隔。任务ID唯一取值范围[1, 100000]执行时长取值范围[1, 10000]。输出对于每组测试数据输出一行排序后的任务ID序列ID之间以空格分隔。我第一次看到这道题的时候觉得挺简单不就是个多重条件排序嘛。但真正动手写了才发现这道题坑点不少而且它考察的能力链条非常完整从输入解析到数据结构选择从排序规则理解到边界条件处理每一个环节都能拉开差距。华为机试的题目往往就是这样表面上不考什么高深的算法但你就是容易写挂。为什么这类题值得反复练因为华为OD机试、校招机试、甚至部分社招机试都很喜欢出这种业务规则复杂数据规模适中的排序题。它不考你背过多少模板而是考你能否在有限时间内把规则翻译成无bug的代码。这道题如果能在15分钟内AC机试的基础分基本就稳了。1.2 考点拆解与难度评估这道题表面上是一道排序题实际涉及的考点至少有五个考点对应题目要求常见失分点多组输入处理T组测试数据只处理了一组就输出导致用例全挂结构化数据存储任务ID/时长/优先级三元组用多个数组存排序时分不清对应关系自定义排序规则三级优先级排序优先级判断顺序写反输出格式控制空格分隔ID序列行尾多了一个空格导致格式错误边界条件N1、ID重复、时长相等排序器比较逻辑不完整出现非法比较如果给这道题定个难度我觉得在华为机试里属于中等偏简单。它不需要任何算法模板动态规划、DFS、二分查找统统用不上纯粹考察基本编程功力和细心程度。但恰恰是这种题在机试中失分的人最多因为越是觉得简单越容易忽略细节。从命题意图来看这道题也很有代表性。真实的工作场景里我们经常需要按照多维度规则对数据进行排序比如报表按部门业绩工号排列、任务队列按优先级提交时间排列。华为机试把这种实际业务中高频出现的需求抽象成考题比单纯考一道快速排序手写实现要贴近生产得多。2. 解题思路拆解三种方案的演进2.1 第一反应三字段依次比较的暴力写法大多数人看到这道题的第一反应是在比较器里把三个条件依次写出来。伪代码是这样的def compare(a, b): if a.priority ! b.priority: return b.priority - a.priority # 优先级降序 if a.duration ! b.duration: return a.duration - b.duration # 时长升序 return a.task_id - b.task_id # ID升序这种写法完全正确也是我最推荐的主流写法。但在真实机试环境中有不少人会在这里翻车把优先级比较写成a.priority - b.priority结果排出来的顺序正好反了。或者忽略优先级相等的情况直接返回0导致排序结果不稳定。还有一个隐蔽的坑是语言差异。C的sort和Java的Arrays.sort对比较器返回值的解释是负数表示a在前Python 3的functools.cmp_to_key也遵循同样的规则。如果换用Python内置的sorted加key参数那整个思路就得转个弯不是写比较器而是构造排序键。2.2 优雅方案构造复合排序键Python选手可以考虑用复合键来实现。核心逻辑是既然优先级要降序、时长要升序、ID要升序那就构造一个元组作为排序键。但要注意元组默认所有字段都是升序所以优先级不能用原始值要取负key (-priority, duration, task_id)这个写法的好处是代码极短、不易出错执行效率也比cmp_to_key高因为底层可以走更快的排序路径。坏处是牺牲了一部分可读性如果团队里有人不熟悉Python的排序机制看到负号可能要反应一下。我用两种方式都实现过实测下来用元组键的方式代码量能减少三分之一而且逻辑更清晰一旦排序规则需要调整比如增加第四个排序字段直接往元组里加元素就行不需要改动比较器的嵌套结构。2.3 数据结构的选型类与字典的取舍接下来说数据存储。我见过不少同学用三个独立的数组分别存ID、时长、优先级然后排序的时候只排ID数组时长和优先级对不上了整个数据就乱了。正确做法是让数据保持结构化。在C里最自然的方式是定义结构体struct Task { int id; int duration; int priority; };在Java里就是写一个类然后实现ComparableTask接口。在Python里可以用dataclass、namedtuple甚至直接用元组都行因为元组天然可以比较。我个人的建议是在机试环境下不要过度设计。Python就直接用元组(priority负值, duration, id)存C就开一个vectorTaskJava就写个内部类并实现compareTo。最怕的是为了优雅搞出一堆继承结构、策略模式结果写代码的时间都耗在类型定义上了。机试的核心原则是能过就行简单直接优先。2.4 复杂度分析为什么排序就够了最后聊聊复杂度这是很多人忽略但面试官很爱问的点。N的最大值是1000T的最大值是10所以单组数据量是1000量级任何O(N log N)的排序算法都能轻松搞定。假设用快速排序或Python内置的TimSort单组排序时间复杂度是O(N log N)十组数据加起来也就十万次比较量级运行时间可以忽略不计。空间复杂度方面存储N个任务的额外空间是O(N)同样没有任何压力。其实这道题如果N放大到10^7量级那就得考虑计数排序或基数排序了因为优先级只有1到10共10种取值。但在当前的题目约束下常规排序就是最优解不需要任何花哨的优化。在机试中判断一道题够不够优化的标准永远是题目给出的数据范围而不是算法竞赛里的极限性能这个思维一定要转过来。3. 核心代码实现与逐段精讲3.1 Python完整实现推荐方案我用Python写了一个可直接AC的完整版本这个版本的风格适合机试场景变量命名直白不做多余抽象import sys def main(): data sys.stdin.read().strip().split() if not data: return idx 0 t int(data[idx]) idx 1 out_lines [] for _ in range(t): n int(data[idx]) idx 1 tasks [] for _ in range(n): task_id int(data[idx]) duration int(data[idx 1]) priority int(data[idx 2]) idx 3 tasks.append((-priority, duration, task_id)) tasks.sort() line .join(str(task[2]) for task in tasks) out_lines.append(line) sys.stdout.write(\n.join(out_lines)) if __name__ __main__: main()这段代码有几个细节处理得比较讲究我逐个说。第一用sys.stdin.read()一次性读入所有数据再按空白切分而不是用input()一行行读、再对每行做split()。这样做的原因是处理多组输入时一次性读入可以避免行尾空白、空行等问题代码也更简洁。机试平台的行尾格式很诡异有时候有\r有时候没有一次性切分就能把这些差异全都抹平。第二直接用(-priority, duration, task_id)作为存储单元把排序规则编码进了元组结构本身。Python元组比较是从第一个元素开始的所以这个元组天然实现了优先级降序、时长升序、ID升序的复合排序。注意优先级那里加了负号这是整个方案最关键的一步。第三输出部分用列表收集每一行的结果最后一次性拼接输出。如果边算边print在数据量大时会有一定的IO开销虽然这道题的数据量下无所谓但养成收集再输出的习惯没有坏处。3.2 另一种Python写法cmp_to_key如果你确实想用比较器的写法Python也支持用functools.cmp_to_key把旧式比较函数转成key函数。我第一次备机试的时候用的是这种写法因为C写多了脑子里全是comparatorfrom functools import cmp_to_key def compare(a, b): if a[2] ! b[2]: # priority return b[2] - a[2] if a[1] ! b[1]: # duration return a[1] - b[1] return a[0] - b[0] # task_id tasks [] for _ in range(n): task_id, duration, priority int(data[0]), int(data[1]), int(data[2]) tasks.append((task_id, duration, priority)) tasks.sort(keycmp_to_key(compare))运行结果完全一样但有两个小问题一是cmp_to_key会有额外的函数调用开销性能比直接比较元组略差虽然N1000时感知不到二是写起来容易出错尤其是比较函数的返回逻辑很多人搞反了升序降序。所以我的建议是如果你平时用Python刷题就直接用元组键方案这是Pythonic的写法也是效率最高的写法。cmp_to_key可以作为理解排序原理的辅助工具但不推荐在机试第一版代码里用它。3.3 C实现结构体与sort用C写的话完整代码如下#include bits/stdc.h using namespace std; struct Task { int id; int dur; int pri; }; bool cmp(const Task a, const Task b) { if (a.pri ! b.pri) return a.pri b.pri; if (a.dur ! b.dur) return a.dur b.dur; return a.id b.id; } int main() { ios::sync_with_stdio(false); cin.tie(0); int T; cin T; while (T--) { int N; cin N; vectorTask tasks(N); for (int i 0; i N; i) { cin tasks[i].id tasks[i].dur tasks[i].pri; } sort(tasks.begin(), tasks.end(), cmp); for (int i 0; i N; i) { if (i) cout ; cout tasks[i].id; } cout \n; } return 0; }C版本需要注意几个点ios::sync_with_stdio(false)和cin.tie(0)是为了加速输入输出这在华为机试中非常重要因为C的cin/cout如果不关同步在处理大数据量时可能超时。sort的第三个参数传入自定义比较函数cmp函数的返回值语义是第一个参数是否应该排在第二个参数前面。另外比较函数最好用常引用传参避免无谓的拷贝。虽然Task只有三个int但别小看这个习惯在比较次数达到千万级时引用传参和值传参的性能差距会很客观。3.4 Java实现Comparable接口Java的实现相对啰嗦一些给出基本框架import java.util.*; public class Main { static class Task implements ComparableTask { int id, dur, pri; Task(int id, int dur, int pri) { this.id id; this.dur dur; this.pri pri; } public int compareTo(Task o) { if (this.pri ! o.pri) return o.pri - this.pri; if (this.dur ! o.dur) return this.dur - o.dur; return this.id - o.id; } } public static void main(String[] args) { Scanner sc new Scanner(System.in); int T sc.nextInt(); while (T-- 0) { int N sc.nextInt(); ListTask list new ArrayList(); for (int i 0; i N; i) { list.add(new Task(sc.nextInt(), sc.nextInt(), sc.nextInt())); } Collections.sort(list); StringBuilder sb new StringBuilder(); for (int i 0; i list.size(); i) { if (i 0) sb.append( ); sb.append(list.get(i).id); } System.out.println(sb.toString()); } } }Java版本中compareTo的返回值语义是负数表示this排在o前面。这里优先级用o.pri - this.pri实现了降序如果写成this.pri - o.pri就反了。Collections.sort内部用的是归并排序的变体TimSort对于普通对象的稳定性有保证效率也不错。StringBuilder的使用也是一个小优化点避免频繁构造字符串导致的内存浪费和性能下降。虽然System.out.println会自动加换行但用字符串拼接大量输出时StringBuilder几乎是必选项。4. 测试用例设计与避坑实录4.1 必测的6类用例写完代码不能直接提交一定要先在本地跑测试。我整理了一组很实用的测试用例覆盖了这道题的主要边界条件输入 2 4 101 5 3 102 3 5 103 5 3 104 3 5 1 99 10 1 期望输出 102 104 101 103 99第一组数据的设计逻辑是102和104优先级都是5且时长都是3按ID升序应为102在前101和103优先级都是3且时长都是5ID升序应为101在前两组之间按优先级降序优先级5的组在前。这个用例能一次性验证三个排序维度的正确性。第二组只有一个任务验证N1的极端情况很多人在这个用例上会因为循环条件写错而崩溃。4.2 两个容易忽略的输入陷阱第一个陷阱是输入中包含多余空行。有些平台的测试用例在数据之间夹杂空行或者最后一行没有换行符。如果用input()逐行读可能出现EOFError或空字符串解析错误。我的方案是用sys.stdin.read()统一读取再切分天然规避了这个问题。第二个陷阱是优先级字段的取值范围是1到10不存在0和负数。这个约束看似无关紧要但如果不小心构造了(-priority)键而priority又有可能是0排序逻辑依然不会出错只是会多一些无意义的键变化。反过来如果题目改成优先级允许负数那元组方案依然成立因为负负得正并不会破坏相对顺序。这个特性让元组键方案比比较器方案更稳健。4.3 实际踩过的三个坑我拿这道题练习的时候一共踩过三个比较典型的坑每个都值得单独说。第一个坑是忽视多组输入的数据读取索引。我刚开始用input()逐行读在处理完第一组数据后顺序读取第二组时index错位导致后面读取的数据全乱了。这个问题的根源是input()读取行时一旦遇到空行代码就报错而平台提供的测试数据格式可能会在组与组之间出现空行。改用一次性读入后问题彻底消失。第二个坑是Python元组键中优先级忘记取反。我第一次写的时候是(priority, duration, task_id)排序结果优先级低的排前面了整个输出和期望正好相反。这个错误其实很好排查只要把三组故意构造的数据跑一遍对比输出就能立刻发现。但如果在考场上一口气写完不测试就提交这种低级错误就会浪费一次宝贵的提交机会。第三个坑是输出格式。题目要求ID之间以空格分隔但不要求行尾多余空格。如果不做处理直接循环打印行尾就会多一个空格。OJ平台对这种格式问题判定很严多余空格直接判WAWrong Answer。我的方案是用 .join(...)彻底避免行尾空格问题。C则用条件判断处理。4.4 常见问题速查表我把这类排序题经常出现的问题整理成了表格方便大家对照检查问题现象解决方案排序顺序相反优先级低的排在前面检查比较器中的降序逻辑元组方案检查负号多组输入只处理一组第二组数据没有输出用循环包裹处理逻辑确认T循环正确空行导致崩溃EOFError或ParseException一次性读入全部数据再切分输出行尾空格格式错误用join或条件判断控制分隔符比较器返回了0排序不稳定补充第三维ID比较确保没有相等元素返回0数据存储混乱排序后数据对应关系错乱使用结构体/类/元组保证数据结构化5. 华为机试实战补充从模拟题到真考场的最后一步5.1 机试平台与考试流程的注意事项华为机试通常是在牛客网或华为自己的平台上进行双机位摄像监控考试环境有严格的限制。我第一次考的时候不太适应平台自带的IDE代码补全和语法高亮都比较简陋所以建议备考生平时就尽量在相似的简陋编辑器里练习不要把日常开发环境比如装了各种插件的VSCode或者PyCharm当成依赖。很多考生问机试能不能用本地IDE写完再粘贴上去答案是可以的但要注意平台对剪贴板的限制有些考试系统禁止复制粘贴或者只允许贴代码不允许贴文本。最好提前去目标平台做一套模拟题熟悉环境再上考场。还有一个细节机试的语言选择。华为机试支持C、Java、Python、Go等主流语言但不同语言在评分上并没有明显差异重点是看你能否AC。我自己推荐Python因为它写起来最快、标准库强大、不容易出编译错误适合在时间紧迫的环境下快速产出可用代码。5.2 时间分配策略与做题顺序华为机试通常是2到3道编程题总分100分或150分不等每道题的分值与难度挂钩。比较稳妥的策略是先通读三道题评估难度先做最简单的题拿到保底分再啃中间难度的题最后留时间挑战最难的题。最忌讳的是在难题上死磕导致简单题没时间写完。以这道模拟题8为例如果它是三道题中的第一道我的建议是15分钟内必须完成包括读题、编码、本地测试、提交。如果超过20分钟还没AC就先标记一下去做后面的题等全部做完再回来补。时间分配的具体比例可以这样参考总时长120分钟三道题的难度分布通常是简单、中等、较难。简单的题控制在20分钟以内中等的题控制在35分钟左右较难的题放到最后去啃最多留45分钟。剩下的20分钟用于检查提交结果和修改bug。5.3 备考刷题方向建议华为机试的高频考点集中在以下几类字符串处理与正则、数组与矩阵运算、自定义排序、哈希表应用、二叉树基础操作、DFS/BFS搜索、动态规划入门、模拟题。其中模拟题和排序题是性价比最高的两个方向因为这类题不依赖算法天赋纯靠练习量就能提升。我建议备考者每天固定写两道题类型交替安排比如一天排序、一天字符串、一天搜索保持手感。每周做一次完整的模拟考试限定时间模拟考场环境做完后认真复盘每道题的思路和代码。这样坚持一个月机试通过率会有非常明显的提升。刷题平台方面力扣LeetCode的Top 100高频题、牛客网的华为机试真题和模拟题都是很好的素材。我的个人经验是把牛客网上的华为机试高频题刷两遍以上第一遍按知识点分类刷第二遍打乱顺序做完整套题这样既保证了知识覆盖又锻炼了临场识别题型的反应速度。力扣上则重点刷排序、数组、字符串、树和动态规划的中等难度题。5.4 代码风格与平时训练建议机试的代码风格和日常开发不一样不需要追求极致的可读性和设计模式但要在保证正确的前提下尽量让代码清晰。核心变量名要语义化不要用a、b、c这种无意义命名。适当的注释可以写但不是必须的。机试评卷只看输出结果不会有人工阅读代码打分所以风格上唯一的标准就是维护成本低、不容易写错。平时训练时我强烈建议开启编译器的所有警告选项让代码在编译阶段就暴露潜在问题。比如C开启-Wall -WextraJava使用增强for循环规避索引越界Python使用类型注解辅助检查。这些小习惯能在正式考试中减少大量低级错误比多刷十道题还管用。另外一定要养成先写测试用例再写代码的习惯。在纸上画出关键用例的输入和期望输出代码写完立刻本地跑一遍确认通过再提交。我认识好几个机试高分的人他们都有一个共同特征花在测试上的时间和写代码的时间差不多。别小看这个习惯它能让你避免大量无谓的罚时。6. 用这道模拟题延伸出的三个拓展思考6.1 从排序题到多条件查询这道模拟题只要求排序输出。如果在这个基础上扩展比如要求输出优先级最高的前K个任务或者按优先级分组统计每组的总执行时长就是一个新的题目了。很多机试真题都会在基础排序上叠加条件考察你对数据结构和STL的灵活运用。比如输出每个优先级组中执行时长排名前2的任务这种变体就需要先用哈希表按优先级分组再对各组分别排序。如果用Pythondefaultdict(list)配合列表推导可以轻松搞定。如果Cmapint, vectorTask也是标准解法。会了基础排序这些扩展都只是加一层壳而已。6.2 当执行时长变为小数或字符串时的处理原题中执行时长是整数比较逻辑非常简单。但如果执行时长变成浮点数比如3.5毫秒和3.20毫秒直接比较浮点数有精度问题。通常的做法是改用Decimal或者把时长统一转换为最小单位比如微秒的整数再比较。如果时长变成字符串比如1h30m那就要在输入解析阶段做一次格式化转换把字符串转成统一的时间数值再参与排序。这个思路在机试中很常见数据格式可以先预处理成统一结构再进入排序。预处理环节做得好排序环节就简单这是很多高手的通用套路。6.3 稳定排序与不稳定排序的影响最后聊一个容易被忽略的概念排序稳定性。Python的sorted和C的sort都不是稳定排序Java的Collections.sort是稳定排序。在原题中因为ID唯一且是最后一个排序维度所以不管排序算法稳定与否结果都是一样的。但假如题目改为输入顺序就是任务的提交顺序在优先级和时长相同的情况下保持提交顺序输出那排序稳定性就成了关键考点。Python中sort虽然不稳定但可以通过key加下标的方式实现稳定效果(priority, duration, index)。Java则直接依赖Collections.sort的稳定性。C则要改用stable_sort。这个思维很容易迁移到真实业务需求中报表需要在按分组排序后保持原始录入顺序数据库分页需要稳定的排序结果这些都是稳定性排序的典型应用场景。机试表面考一道排序题背后其实考察的是你对排序底层原理的理解深度。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价