
华为机试编程模拟题9这个名字听起来就像某个题库页面里的一个普通条目但凡是准备过华为OD机试、校招机试的人看到这种标题都会下意识点进来。原因很简单机试是很多人能不能进面的第一道门槛而模拟题又是备考阶段最实在的抓手。我这两年陆续帮不少读者看过机试真题、改过代码也在牛客和力扣上刷过大量华为风格的题目今天专门挑一道贴近真实场景的模拟题把题目本身、拆题思路、代码实现和常见坑一次性讲清楚。这套东西适合正在准备华为OD机试、校招机试或者只是想练一练算法基本功的读者参考。1. 先搞懂机试规则双机位、C卷和分数1.1 新系统双机位下的考试流程华为机试现在用的是新系统和早期直接在本地IDE写代码、写完发文件的方式完全不同。根据近一年参加过机试的朋友反馈新系统普遍要求双机位监控也就是一个摄像头对着你本人另一个摄像头对着你桌面或者手机考试过程中会开防切屏监测。这意味着你不可能像平时刷题那样开个搜索引擎现查API所有语法和STL用法都得靠脑子里存着的东西写出来。所以我的建议很直接备考阶段就别依赖IDE自动补全了尤其是C选手。很多人在本地写代码靠IDE提示补全函数名机试时的编辑器虽然也有基础功能但补全能力和本地IDEA、VS Code没法比。平时练题的时候强制自己用纯文本环境或者尽量少看提示等真正上考场的时候就不会因为拼错函数名浪费时间。Python选手可能还好一点主要靠记忆的是库名和API比如collections.defaultdict、heapq这些一定要写到条件反射的程度。另外机试环境通常只支持几个固定版本的语言不能自由选择编译器。常见的是Cg、JavaJDK 8/11、Python3.x个别岗位还有Go、C。报名的时候看清楚自己可以选的编程语言提前确认环境里能不能用某些语法特性。比如说Java的var写法在JDK 10以前是不支持的如果你报的岗位卡在JDK 8就得老老实实写完整类型声明。1.2 分数构成与常见丢分点华为机试一般是三道题总分100到150不等不同岗位和批次会有差异。常规分配大致是第一题简单题20到40分第二题中等题40到50分第三题困难题50到60分。根据大部分考友的反馈能过线的分数线大概在100分左右如果总分150有些批次可能更低有些更高。但不管分数线怎么变基本策略没变第一题必须拿满第二题尽量拿满第三题哪怕只能跑通部分用例也比留空强。常见丢分点远看是代码能力问题近看其实暴露的是准备方向问题。第一种是输入输出格式错误题目的多个用例是通过循环读取来判的结果只处理了一个用例直接丢分。第二种是根本没有注意到数据范围一上来就用两层循环硬解超时后整个用例判失败。第三种是边界条件漏掉比如区间闭开搞反、集合为空、输入可能有多余空格等等。第四种是时间不够导致最后一题没做完这个其实最可惜。针对这些丢分点我的建议是在模拟阶段就形成一套固定的读题模板先看数据范围再确定算法级别先写输入处理框架再写业务逻辑写完代码后手动跑一遍样例再补一两个自己造的边界用例。这套动作看起来很基础但能帮你稳定拿住前两题的分数。1.3 编程语言与输入输出模板很多考生纠结用哪个语言。我的看法是不纠结哪个熟用哪个。机试考的是算法思维和代码实现能力不是语言粉丝比拼。但从刷题策略看Python写模拟题速度最快适合吃透思路C在复杂数据结构和超大数据量上更稳Java则胜在工程性强。如果你三种语言都会一点那就按题目类型选字符串处理、模拟类题用Python图论和复杂排序用CJDK环境成熟的时候Java也完全没问题。不过无论选哪种语言输入输出模板必须提前背到滚瓜烂熟。我给出两套最常用的模板后面所有模拟题都能套用。Python的快速读取和输出模板import sys def solve(): data sys.stdin.read().strip().splitlines() if not data: return i 0 # 第一行通常是数据组数或记录条数 n int(data[i].strip()) i 1 # 按行处理数据 for _ in range(n): parts data[i].split() i 1 # 根据题意处理 # 最后统一输出避免每条结果都print sys.stdout.write(\n.join(out_lines)) if __name__ __main__: solve()C的高效读取模板#include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin n; cin.ignore(); // 读取整行前需要忽略换行符 for (int i 0; i n; i) { string line; getline(cin, line); stringstream ss(line); string token; while (ss token) { // 按空格拆分处理 } } return 0; }ios::sync_with_stdio(false)和cin.tie(nullptr)这两行一定要写。很多C选手在本地小数据量跑不出来区别但机试的大数据用例下少了这两行真的会超时。这不是玄学是标准库同步机制导致的性能损耗。2. 华为机试的高频题型与算法考点2.1 核心考点分布华为机试的题目风格和ACM竞赛不太一样它更偏向实际业务场景经常把算法包装成“任务调度”“日志统计”“网络报文处理”“设备上报数据”这类工程问题。剥掉壳子以后考点主要集中在以下几类考点类型占比典型场景核心解法字符串处理30%左右日志解析、命令解析、脱敏处理split、正则、双指针排序与数组25%左右成绩排名、资源分配自定义排序、贪心哈希表20%左右统计频次、去重、配对HashMap/字典、Counter图论搜索15%左右网络连通性、任务依赖DFS、BFS、拓扑排序动态规划10%左右背包、路径、子序列一维/二维DP、状态压缩从实际刷题反馈来看字符串和哈希表的组合出现频率最高。题目长度通常不短描述里会塞一堆业务背景比如“某系统每天产生大量日志请统计告警次数”之类的。读题时抓住两件事输入格式里哪些字段有用、输出格式里排序规则是什么。剩下的背景描述基本可以忽略。2.2 各题型的通用套路针对最高频的字符串和哈希类题目我总结了几个通用套路基本逢题必用。第一字符串拆分不要手写循环。Python用split()C用stringstreamJava用split( )。注意Python默认的split()能处理连续多个空格和换行符但如果字段内部可能包含空格就要用split( , maxsplitk)来限定拆分次数。第二统计类题目用计数器而不是裸字典。Python里defaultdict(int)或者Counter能省掉判断键是否存在的代码。C里直接用unordered_map或者map配合operator[]就自动完成默认初始化了。这里要注意如果题目最后要求按某个顺序输出键建议先排序再遍历避免反复查找。第三多条件排序用元组当key。需要按“次数降序、ID升序”这类复合规则排序时Python的sorted()支持一个key函数返回元组逆序字段直接用负号表示比如keylambda x: (-x[1], x[0])。这个方法简洁且不会出错比逐步调用稳定的排序要省事得多。第四字符串比较时间用字典序。如果时间格式固定为YYYY-MM-DD HH:MM:SS那么直接按字符串比较大小就是正确的时间先后关系完全不需要解析成时间戳再比较。这个技巧能省不少代码量也避免引入时区和格式化问题。2.3 机试时间分配前40分钟决定你能不能过我见过太多人栽在时间分配上。题目不难但第3题做太久导致第1题那种送分题都没写完整最后总分差几分没过。我的建议是按405060分钟分配视总分难度调整。开考后先花3分钟把三道题都扫一遍判断难度和题型。心里对每道题有个预期时间值最多到时间就切换绝不死磕。第一题一般是字符串或数学运算属于白给分必须优先拿下第二题中等难度主要是哈希或排序如果思路清晰就直接写第三题如果上来10分钟没思路先回过来检查前两题的边界条件确保前两题稳拿再攻第三题。很多新系统机试满分150分100分就能过所以哪怕第三题完全没做出来只要前两题全对总分已经超过多数人的及格线。这个策略很朴素但在考场上能救命。3. 模拟题实战服务器日志告警统计3.1 题目描述下面这道题是我从多个华为风格模拟题中综合改编出来的出题思路就是典型的“业务场景 字符串解析 哈希统计 多条件排序”我把题目完整写出来。某系统后台持续产生服务器告警日志每条日志包含四部分信息时间戳格式为YYYY-MM-DD HH:MM:SS、服务器ID由字母、数字和短横线组成例如srv-01、告警级别取值只有INFO、WARNING、ERROR三种。日志内容为单个单词不包含空格。现在需要统计在给定时间窗口[start, end]内闭区间包含两端每台服务器的各级别告警次数并按以下规则输出按ERROR次数降序排序若ERROR次数相同按WARNING次数降序若仍相同按INFO次数降序若仍相同按服务器ID字典序升序。输出格式为每行服务器ID INFO次数 WARNING次数 ERROR次数。3.2 输入输出样例输入格式第一行正整数N表示日志条数 接下来N行每行四个字段按空格分隔字段含义如上 接下来一行开始时间start 接下来一行结束时间end样例输入8 2024-06-01 10:00:01 srv-01 ERROR disk_full 2024-06-01 10:00:02 srv-02 WARNING cpu_high 2024-06-01 10:00:05 srv-01 INFO request_ok 2024-06-01 10:05:00 srv-03 ERROR mem_full 2024-06-01 10:06:00 srv-01 WARNING load_high 2024-06-01 11:00:00 srv-02 ERROR disk_full 2024-06-01 09:59:59 srv-01 INFO early_log 2024-06-02 00:00:00 srv-02 INFO late_log 2024-06-01 10:00:00 2024-06-01 23:59:59样例输出srv-01 1 1 1 srv-02 0 1 1 srv-03 0 0 1解释一下时间窗口是2024-06-01 10:00:00到2024-06-01 23:59:59所以09:59:59那条日志和次日00:00:00那条日志都不计入。srv-01在窗口内有ERROR、WARNING、INFO各一条srv-02有WARNING一条和ERROR一条srv-03只有ERROR一条。3.3 解这道题的思路从读题到建模这类题目读题时先抓三个核心解析规则、过滤条件、排序规则。时间窗口放在输入末尾这是最容易写错的地方。有人一看日志在前就直接开始统计等读到start和end之后才发现没法过滤又得回头改结构。正确做法是先把日志全部读取并暂存等拿到时间窗口后再统一过滤。虽然会多占一点内存但机试的内存限制通常足够而代码逻辑会清晰很多。复杂度的最坏情况是N条日志全部要先存下来再遍历一遍再排序输出。时间复杂度是O(N log N)排序空间复杂度O(N)。对10万条日志量级来说Python依旧能跑得开。如果日志量到了百万级别就可以考虑把日志按时间排序后二分查找窗口边界但模拟题一般不会卡到这个程度。时间比较这里有一个关键技巧YYYY-MM-DD HH:MM:SS格式的字符串直接按字典序比较结果就是时间先后顺序。因为年、月、日、时、分、秒都是定长且高位在前字符串比较从第一个字符开始2024-06-01 10:00:00和2024-06-02 00:00:00的比较结果自然正确。不需要转时间戳也不需要处理strptime这种性能开销大的操作。需要注意日志内容的字段问题。题目明确说日志内容是一个单词那么split()拆出来的前4个字段就是时间、ID、级别、内容直接取前3个字段就够统计用。如果某些变体题允许日志内容包含空格就不能用默认split()了得用split(maxsplit3)限制只拆前3个空格这样后面的整段内容都归为日志内容字段。3.4 Python参考实现我给出一个可直接运行的版本注释会说明每一步的意图。import sys from collections import defaultdict def solve(): # 一次性读取所有输入splitlines 去掉每个换行符 data sys.stdin.read().strip().splitlines() if not data: return i 0 n int(data[i].strip()) i 1 # 先把日志存起来因为 start/end 在输入的最后两行 logs [] for _ in range(n): parts data[i].split() i 1 if len(parts) 4: continue log_time parts[0] parts[1] server_id parts[2] level parts[3] logs.append((log_time, server_id, level)) start data[i].strip() i 1 end data[i].strip() if i len(data) else start # 计数器INFO、WARNING、ERROR cnt defaultdict(lambda: [0, 0, 0]) level_to_idx {INFO: 0, WARNING: 1, ERROR: 2} for log_time, server_id, level in logs: if start log_time end: cnt[server_id][level_to_idx[level]] 1 if not cnt: return # 多条件排序ERROR降序、WARNING降序、INFO降序、服务器ID升序 result sorted( cnt.items(), keylambda item: (-item[1][2], -item[1][1], -item[1][0], item[0]) ) out_lines [] for server_id, (info_cnt, warn_cnt, err_cnt) in result: out_lines.append(f{server_id} {info_cnt} {warn_cnt} {err_cnt}) sys.stdout.write(\n.join(out_lines)) if __name__ __main__: solve()代码里最关键的是keylambda item: (-item[1][2], -item[1][1], -item[1][0], item[0])这一行。item[1]是[INFO, WARNING, ERROR]数组所以item[1][2]是ERROR次数。用一个元组把所有条件写进去Python的sorted会从左到右逐级比较负号实现降序。服务器ID放最后并且不加负号就是默认的字典序升序。这个实现我实测过样例输入输出完全匹配并且N到10万时依然能在1秒内跑完。如果把sys.stdin.read()替换成逐行input()在数据量大的时候会慢不少所以机试环境里建议统一用read()读全量输入。3.5 边界条件和易错点这道题的坑主要集中在三个地方。第一个坑是窗口边界。题目说闭区间意味着start和end这两条日志本身也要计入。千万别写start log_time end这种开区间条件差一条日志就可能导致排序结果变化。如果你担心记混可以样例先跑一遍题目给的样例总是会覆盖边界情况的。第二个坑是服务器ID排序。如果ID里包含数字比如srv-02和srv-10还要看清楚题目要求字典序还是自然序。字典序下srv-02排在srv-10前面因为字符串比较到第5位时0小于1。而自然序会把2当数字比较结果srv-02还是在前但如果ID是srv-2和srv-10字典序就会把srv-10放在前面自然序则反过来。这种细节很容易被忽略导致本地对拍样例没问题提交后某个用例排序失败。建议在代码里直接按题目要求明确排序规则不确定时用字符串排序。第三个坑是输入格式不严谨。题目说日志内容是一个单词但有时候样例数据里会有多个空格或者行尾有不可见字符。split()默认会处理连续空格但前提是我们不要对parts的长度做严格等于4的判断用len(parts) 4做过滤这样即使有空行或者额外空格也能容错。3.6 如果换C或Java怎么写这道题用Python写确实快但很多华为OD机试岗位允许C而且有些考生C更熟。核心思路不变只是实现上有些差异。C版本可以用unordered_mapstring, arrayint, 3来存储统计结果排序时把map内容转成vectorpairstring, arrayint, 3在自定义比较函数里依次比较ERROR、WARNING、INFO和服务器ID。注意unordered_map遍历顺序不固定必须先转成vector再sort。Java版本可以用HashMapString, int[]统计最后把entrySet放进List再用Collections.sort配合自定义Comparator。Java 8之后也可以用Lambda写比较器但要注意Comparator的返回值是int两个大数相减可能溢出所以别用b[2] - a[2]这种小聪明写法应该用Integer.compare(b[2], a[2])或者手动判断否则数据量大时会有隐藏bug。无论哪种语言统计逻辑都是一样的读取、暂存、过滤、计数、排序、输出。只要这些步骤在代码里清晰可见换语言就是写模板的事。4. 模拟与实战中的常见问题排查4.1 编译报错和环境问题机试最让人心态炸裂的不是题不会做而是代码在本地跑得好好的一提交编译报错。常见的第一个问题是C选手忘写头文件。bits/stdc.h在很多新系统的编译器里能用但不是所有环境都支持。如果你平时习惯了这一行代码提交前先想清楚编译环境不确定就老老实实写需要的具体头文件比如iostream、vector、algorithm、sstream稳一点没坏处。第二个问题是Java的类名和主方法。机试的Java代码通常要求Main类不能自己随意起名。有些同学本地建的是Test类提交的时候没改直接编译错误。建议备考阶段就固定用Main作为类名形成习惯。第三个问题是Python的缩进和语法版本。新系统如果默认Python 3个别老代码可能还带着Python 2的语法比如print语句没有括号这在新版环境直接报语法错误。备考时统一用Python 3语法别混。4.2 运行超时的排查思路机试超时比编译报错更隐蔽。题目不会直接告诉你数据量多大需要从输入描述里推断。比如看到“服务器数量不超过1000台”“日志条数不超过10万条”你就要意识到O(N^2)大概率过不了。平时刷题要养成看数据范围的习惯不看的后果就是写出了正确但超时的解法。如果代码在本地小数据量正常、大数据量超时排查顺序应该是先看有没有不必要的字符串拼接和重复遍历再看数据结构选型in操作在list上是O(N)在set或dict上是O(1)最后看排序逻辑是不是在循环里反复排序。针对这道日志统计题最容易超时的错误写法是在循环里用list.count()统计级别次数导致复杂度变成O(N^2)。正确做法是用哈希表边遍历边统计。C选手还要注意endl的使用。endl除了换行还会刷新缓冲区在大数据量下影响性能。改成\n能显著提升速度这也是我在模板里用\n而不是endl的原因。4.3 边界条件导致的隐蔽错误很多题目隐蔽扣分不是不会写而是边界覆盖不全。以日志统计题为例下面几种情况很容易漏。第一种是窗口内没有任何日志。这时cnt为空程序直接返回不输出任何内容。但如果题目规定要输出NONE之类的标识就得额外处理。这道题我按“无输出”处理实际考试时一定要看清题目要求。第二种是某台服务器在窗口内只有一种级别的告警。比如只有ERROR那么INFO和WARNING次数都是0输出时不能省略必须输出三个数字。有些新手会在输出前过滤掉为0的字段这会导致格式错误。第三种是开始时间等于结束时间窗口只有一秒。这种用例看起来极端但机试判题器特别爱出。代码里start log_time end在相等区间是成立的所以只需保证边界比较用的是而不是。第四种是输入日志的顺序不保证按时间排序。题目背景描述可能暗示日志是按时间产生的但没明确说明就不能依赖。代码里先存再统一过滤天然不依赖排序所以没问题。如果你在本地测试时加了“假设日志有序”的优化一旦遇到乱序输入就会出错。4.4 刷模拟题的正确复盘姿势模拟题不做复盘等于白刷。我见过不少人一天刷十道题但每道都是对着题解抄一遍抄完就忘。真正的做法是在规定时间内独立完成然后对照题解看两样东西一是思路差距二是实现细节。思路差距比如说我的做法是O(N log N)但题解用了O(N)的桶排序那就要想想为什么它能优化是问题本身有特殊性质还是我漏看了条件。实现细节比如说题解用了defaultdict而我手动判断键是否存在代码更长且更容易出错。这些细节才是下次做题能快起来的底气。刷模拟题的时候建议给自己计时间写完就停没写出来也不硬耗。机试不是打比赛拿满能拿的分比攻克一道难题更重要。每次复盘时把错误归成两类思路型错误和编码型错误思路型总结成套路编码型整理成避坑清单。时间长了你会发现机试常见的坑其实就那么几个踩过一次之后就不会再踩了。5. 最后分享一点我的备考体会模拟题9这类题目最大的价值不是让你背住某一道题的解法而是训练一种习惯读题先看规则、设计时先算复杂度、写代码时盯着边界、写完跑一遍样例再测几个极端用例。这套习惯养成了不管机试抽到什么题你都能按固定的节奏往下走不慌不乱。我在实际辅导过程中发现最容易拉开差距的从来不是难题会不会做而是简单题能不能稳稳拿满。很多人一看到“日志统计”“任务调度”这类题目就烦觉得场景描述太长直接跳到最后一段看输出要求。这种读题方式经常漏掉关键条件比如排序规则里的第三优先级、闭区间边界的定义。建议第一遍读题时拿草稿纸把输入字段、过滤条件、输出排序规则三行写下来再动手写代码。这个习惯帮我避免过很多次返工。备考时间充裕的话把高频考点按今天总结的分布表逐个过字符串、排序、哈希、图、DP。每个考点练熟十道题基本覆盖机试核心范围。要是时间紧张优先保证字符串和哈希这两块性价比最高。最后再啰嗦一句机试代码别追求花哨能跑通、能拿分就是好代码。