☰
华为2016研发工程师编程题真题复盘:三道经典题与核心考点全解析
2026/10/24 6:35:31 网站建设 项目流程

2016年秋天,我参加华为研发岗校招笔试时的那个场景,到现在还记得很清楚。三道编程题,两个小时的在线OJ考试,全屏监控,期间不能切出页面,第二题我改到最后一分钟才提交,交完手心全是汗。后来这几年,我陆续帮学弟学妹辅导华为的机试,又回头把2016年那套题翻出来重做了一遍,才发现这批题出得相当有水平——表面上看都是基础题,实际上每一道都在筛工程思维,而不是筛刷题数量。这篇文章我就把华为2016研发工程师编程题的考察逻辑、三道经典真题复盘、核心考点拆解和踩坑经验完整梳理一遍,正要投华为研发岗、或者准备华为OD机试的同学,可以拿来当一份备赛参考。

1. 2016年华为研发岗机试,到底在考什么

先说整体情况。2016年华为研发工程师校招在线笔试,常规配置是三道编程题,时间两个小时,平台是华为自研的OJ系统,支持C/C++/Java等主流语言。题量不大,但时间也不算宽裕,尤其是第三题如果在字符串处理上卡住,后面基本没有回头检查的机会。从题目风格上看,三道题分别覆盖了字符串处理、数学模拟、数据统计三个方向,没有图论,没有复杂的动态规划,也没有需要背诵模板的高级数据结构。

当时很多人觉得华为笔试简单,无非就是考字符串、数组、循环。这个说法对了一半。题目本身确实不偏不怪,但它和你平时在LeetCode上刷的Medium题完全是两套逻辑。华为的题更贴近工程场景:处理日志里的错误记录、解析输入的格式、把物理世界的问题转化成代码逻辑。它考的不是你背了多少算法模板,而是你能不能把一个具体问题拆成计算机能执行的步骤,并且把边界条件处理干净。

还有一个容易被忽略的点:华为的OJ判题是按测试用例给分的,通过了多少用例就得多少分,不是一锤子买卖看是否AC。这意味着你哪怕只写出了暴力解法,只要能把简单用例跑过去,就能拿到一部分分数。这个设计其实非常工程化——真实项目里,先跑通再优化,永远比憋一个大招强。所以策略上,遇到卡壳的题,先把暴力版写出来拿分,比死磕最优解明智得多。

再说说备考方向。2016年的题目风格,在之后的华为校招和OD机试里延续了很久,尤其是“字符串+统计+边界条件”这个组合,几乎是每场必考。后面有一年出的“简单错误记录”,和2016年的第三题在考点上一脉相承。所以这份真题复盘,不只是回望一份老卷子,它对当下备考华为机试的人,仍然有直接的参考价值。

2. 三道经典真题复盘:代码与思路全解

2.1 第一题:删除字符串中出现次数最少的字符

这道题在很多华为机试经验帖里都出现过,属于典型的“送分但不完全送分”题。题目要求是:输入一个字符串,统计每个字符出现的次数,找出出现次数最少的字符,把这些字符从原字符串中删除,输出删除后的结果。如果多个字符出现次数一样且都是最少,则把它们全部删除。输出时保留原字符串中其他字符的相对顺序。

举个例子:输入abcdd,a、b、c各出现1次,d出现2次,出现次数最少的是1次,所以a、b、c都要删掉,最后输出dd。

这题的常规解法分三步:第一步,用数组统计每个字符的频率;第二步,遍历频率数组找到最小频率;第三步,再一次遍历原字符串,只输出频率大于最小频率的字符。

#include <stdio.h> #include <string.h> int main() { char s[21]; while (scanf("%s", s) != EOF) { int count[26] = {0}; int len = strlen(s); for (int i = 0; i < len; i++) { count[s[i] - 'a']++; } int min = 20; for (int i = 0; i < 26; i++) { if (count[i] > 0 && count[i] < min) { min = count[i]; } } for (int i = 0; i < len; i++) { if (count[s[i] - 'a'] > min) { printf("%c", s[i]); } } printf("\n"); } return 0; }

这题有两个地方容易翻车。第一个是统计频率时用的数组下标,如果题目输入包含的不是小写字母,而是大写字母或者空格,直接用s[i] - 'a'就会越界。我当时做的时候多留了个心眼,用了一个长度256的数组,直接用字符的ASCII码做下标,这样大小写、数字、特殊字符都能覆盖。第二个容易错的地方是“出现次数最少的字符”的定义。比如字符串是aabbc,a和b都出现2次,c出现1次,那c被删掉,输出aabb;但如果所有字符出现次数一样,比如abc,那a、b、c都要删掉,输出空行。空行也是要输出的,很多人在这里漏了换行符,导致格式不对。

复杂度方面,这个解法的时间复杂度是O(n),空间复杂度O(1),在机试环境下完全够用。这道题我觉得还有一层考察意图:它要求你两次遍历字符串,第一次统计,第二次过滤,这种“先统计再处理”的思路,在后面的错误记录题里又出现了一次,算是个铺垫。

2.2 第二题:空瓶换汽水,一道隐藏的递归题

第二题是华为OJ里的经典题“汽水瓶”:三个空汽水瓶可以换一瓶汽水,假设小明手上有n个空瓶,问最多能喝到多少瓶汽水。输入包含多组数据,每组数据占一行,输入0表示结束。题目里还有个特殊规则:如果最后剩下2个空瓶,可以向老板借一个空瓶,凑够3个换一瓶汽水,喝完后把3个空瓶还给老板,相当于2个空瓶也能换来一瓶汽水。所以输入2时,答案是1。

我当年第一次看到“2个空瓶还能借一个换一瓶”这个规则时,第一反应是怀疑题目出错了,后来才意识到这是在考一个非常朴素的“递归/贪心”思维。每3个空瓶换1瓶,喝掉后那1瓶又变成1个空瓶,所以每换一次,手上的空瓶数量变化是:n = n / 3 + n % 3。累加每次换到的瓶数,直到剩下的空瓶数小于3。最后如果剩2个空瓶,答案再加1。

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { int n = in.nextInt(); if (n == 0) break; int total = 0; while (n >= 3) { int exchange = n / 3; total += exchange; n = n % 3 + exchange; } if (n == 2) { total++; } System.out.println(total); } } }

这题用递归写会更好理解,但直接用循环也不复杂。核心是搞清楚状态转移:喝完的汽水瓶会重新变成空瓶,进入下一轮交换。只要这个状态转移想清楚了,代码三到五行就能写完。很多人卡住是因为把问题理解成了“有多少个空瓶就除以3”,忽略了“换来的汽水喝完后又产生新空瓶”这个循环。

这题还有一个人人都会踩的坑——多组输入。题目要求输入0结束,所以while循环条件必须是“读入成功且n不为0”。我见过不少同学用while(true)读,然后忘了处理0的情况,结果死循环或者多输出一行。在华为的OJ系统里,多输出的任何一个多余字符都可能被判格式错误,这种错误最冤。

2.3 第三题:简单错误记录,数据统计题的经典模板

第三题是整套卷子里最有含金量的一道,也是后来被反复引用的一道题。题目大意是:模拟一个错误记录系统,输入若干行“文件路径 行号”,每遇到一条重复的记录(文件路径同名且行号相同,路径不同但文件名相同算同一条),计数加1。最后输出所有记录中最后出现的8条,每条记录需要输出文件名(不带路径)、行号和出现次数。还有一个细节:如果文件名超过16个字符,只取最后16个字符。

这题我当年做的时候,第一反应是定义一个结构体数组,里面存文件名、行号、次数、是否出现过,然后每来一条记录就遍历一次数组,看有没有重复的。但转念一想不对,如果输入量一大,每次都遍历整个数组,时间复杂度会变成O(n²),很可能超时。正确的做法是直接用哈希表,Java里用HashMap,以“文件名+空格+行号”作为key,出现次数作为value。同时为了保证输出顺序是“按第一次出现的先后顺序”,需要使用LinkedHashMap。

import java.util.LinkedHashMap; import java.util.Map; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); Map<String, Integer> map = new LinkedHashMap<>(); while (in.hasNext()) { String file = in.next(); int line = in.nextInt(); String name = file.substring(file.lastIndexOf('\\') + 1); if (name.length() > 16) { name = name.substring(name.length() - 16); } String key = name + " " + line; map.put(key, map.getOrDefault(key, 0) + 1); } int count = 0; int start = map.size() > 8 ? map.size() - 8 : 0; for (Map.Entry<String, Integer> entry : map.entrySet()) { if (count >= start) { System.out.println(entry.getKey() + " " + entry.getValue()); } count++; } } }

这题有好几个值得记一辈子的教训。第一个是解析文件名,输入的路径是用反斜杠\分隔的,在Java字符串里要写成"\\\\",也就是lastIndexOf('\\\\')。我当时忘了转义,写成了lastIndexOf("\\"),导致路径永远切不开。不过如果你用file.substring(file.lastIndexOf('\\\\') + 1),这个就对了。第二个坑是“取最后16个字符”,很多人想当然取了前16个字符,这题就全错了。为什么是最后?因为在完整路径里,通常越靠后的字符越能区分文件,这个设计其实挺符合真实日志系统的逻辑。第三个坑是输出最后8条,不是输出次数最多的8条,而是“最后出现/最后存储”的8条,所以用LinkedHashMap保证插入顺序,然后取最后8个。

这题本质上是个综合题,字符串解析、哈希表、边界条件、输出顺序控制,全揉在一起了。但它的每一层拆开看都不难,难的是你能不能在紧张的考试时间里把每一层都处理好。这也是为什么华为年年都出类似的题——工程里最贵的能力,就是把一堆简单环节高质量串起来。

3. 核心考点拆解:华为机试的涨分点在哪

3.1 字符串处理,工程场景的第一生产力

翻遍华为历年机试题目,字符串处理出现的频率高得吓人。删除最少字符、错误记录、单词倒排、简单密码、字符串排序,几乎是轮着来。原因也简单:研发工程师日常面对的是日志、配置、协议报文、用户输入,这些全是字符串的天下。你写一个接口,要解析参数;你调一个服务,要处理返回的JSON;你排查一个线上问题,要抓日志、过滤关键字。字符串处理能力,基本等于工程基础的下限。

常见的字符串操作就那么几个:按分隔符切分、查找子串、统计字符频率、大小写转换、截取指定长度、去空格、拼接。这些操作在C语言里要小心'\0'和数组越界,在Java里要熟悉substring、indexOf、lastIndexOf、split的边界行为。我建议准备华为机试的人,把字符串常用API从头到尾过一遍,然后专门练几道字符串题,做到不用查文档也能写出来。考试时每查一次文档,都是在消耗宝贵的倒计时。

3.2 边界条件,过不过用例就看这一步

很多同学刷题时喜欢直接看题解,觉得自己“思路对了就行”。但在华为机试这种按用例给分的场景里,思路对了但边界条件没处理干净,照样拿不到分。我总结了一下,华为机试最爱考的边界条件有三类:空输入、单元素输入、极值输入。比如删除最少字符那题,输入只有一个字符,删完是空串,空串也要输出一个换行;汽水瓶那题,输入最大是100个空瓶,要保证循环不会死循环;错误记录那题,输入文件名为空,或路径没有反斜杠,要保证代码不抛异常。

训练边界条件最好的方法,是读完题之后先不急着写代码,在草稿纸上列几个测试用例:正常情况、只有一个元素的情况、极端输入、重复输入、没有匹配结果的情况。把这些用例在心里跑一遍,再开始写代码,写完之后用这些用例逐条验证。这个方法看起来很笨,但实战效率极高,能帮你把至少30%的隐性扣分点提前干掉。

3.3 输入输出,细节决定AC还是WA

华为OJ的输入输出格式要求非常严格,多一个空格、少一个换行、输出顺序不对,都可能被判错误。我见过最多的翻车现场是:判题时要求“每组输出占一行”,有人却把多组输出全挤在一行里;或者题目说“输入包含多个测试用例,每个用例用空格分隔”,有人读入时用了nextLine(),把同一行的数据拆成两行了。

关于输入输出的处理,有一个基本原则:用OJ平台支持的Scanner或scanf读取,按题目描述逐项读取,不要试图一次读一整行再手动拆分,除非你确定格式非常规整。另外,输出统一用println或者在printf里手动加\n,千万不要在双引号字符串里随手打多余空格。对了,如果题目说要“输出最后8条”,但实际记录不足8条,那就全部输出,这个逻辑也要写对。

4. 实操过程:从读题到AC的完整流程

4.1 动笔之前,先花3分钟列测试用例

我后来给学弟学妹做模拟面试时发现,很多人在机试里犯的错误,根源不是代码能力,而是读题太快。题目还没看明白输入输出的格式,就开始敲代码,敲到一半发现理解错了,删掉重写,时间就浪费了。高效的读题顺序应该是:先看输入格式,再看输出格式,然后看题目正文,最后看样例。为什么要先看输入输出?因为输入输出决定了你的代码骨架,先把骨架搭对,再往里填业务逻辑就不会跑偏。

看样例的时候,不要只盯着样例本身,要思考样例背后的规则。比如汽水瓶那题,样例里出现了2 -> 1,这个用例就是在提示你有“借空瓶”的规则。如果你没注意到这个样例,只在循环里写n / 3,那这个用例就过不了。列测试用例的过程,其实是在和出题人对话,把出题人设置的所有隐藏规则都挖出来。

4.2 代码实现阶段的三个关键习惯

我自己的习惯是,写代码前先定义一个清晰的函数边界。哪怕是机试这种场景,我也会把核心逻辑封装成一个单独的函数,比如int maxBottles(int n)、String removeLeast(String s),主函数只负责读取输入和调用函数。这样做的好处是,测试的时候可以直接在main里传不同的参数验证函数逻辑,不用一遍遍手动输入;而且万一函数写错了,排查范围也小很多。

第二个习惯是变量命名要短但语义清晰。机试不要求你写多优雅的代码,但变量名至少不能自己都看不懂。count、min、exchange、total这种一眼能看懂的命名就够了,别用a1、b2这种注水命名,半小时后回看代码,你自己都不知道a1是什么。

第三个习惯是写的过程中随时静态检查。写完一个循环,马上在脑子里跑一遍:第一次循环的值是什么,最后一次循环的值是什么,循环会不会越界,会不会死循环。把这三个问题想清楚,再写下一个模块。这个习惯能帮你拦截掉至少一半的运行时错误。

4.3 本地调试与在线OJ的差异

本地IDE调试通过了,提交到OJ上却报错,这个场景几乎每个人都会遇到。最常见的差异是:本地IDE可能自动帮你处理了编码问题,而OJ的判题系统是纯命令行环境,对字符编码很敏感;本地IDE能容忍你多输出一个提示语句,而OJ只认标准输出里的预期内容。所以提交前,一定要把代码里的调试输出(比如System.out.println("debug " + count))全部删掉,不然这些多余的输出会被判题系统当成答案的一部分。

另一个差异是内存和运行环境。Java在OJ上运行时,类名必须是Main,主类不能带public之外的其他修饰符,否则直接编译失败。C语言要注意scanf读字符串时是否会读入换行符,以及数组开得够不够大。我建议考试前用华为OJ的模拟环境完整走一遍流程,确认自己的编译命令和运行方式都没问题,再上考场。

5. 常见问题排查与避坑实录

5.1 高频问题速查表

我把这些年带人准备华为机试时,大家最容易遇到的报错和异常整理成了一张表,遇到问题先对照排查。

问题现象可能原因解决办法
本地运行正常,提交后输出为空类名不是Main,或主方法签名错误Java类名改成Main,确认public static void main
输出内容和预期一模一样,但判错多余的空格或换行,或输出顺序不对用文本对比工具逐字符比对,检查循环输出顺序
运行时抛ArrayIndexOutOfBounds字符减'a'后不在数组范围内改用长度256的数组,直接用ASCII码做下标
程序超时用了O(n²)遍历,或死循环改成哈希表/标记数组,检查循环退出条件
多组输入只读了一组没有正确判EOF或结束标志C用while (scanf != EOF),Java用hasNext
文件名截取结果不对反斜杠转义写错,或截取了前16位用lastIndexOf('\\\\'),取子串时用substring(len-16)
空输入或空行导致崩溃没有判空读入后先判断字符串长度,长度为零直接continue

这张表里的每一个场景都是真实发生过的,而且基本都是“思路对但细节翻车”的类型。考试时时间紧,不可能一个个试错,所以平时练习就要把这些坑提前踩一遍,踩过了就不会再踩。

5.2 三个我亲历的崩溃瞬间

第一个崩溃瞬间发生在汽水瓶那道题。我一开始写的是递归版本,逻辑是对了,但递归函数里忘了写终止条件,导致到了n=1的时候还在递归,直接栈溢出。后来改成循环版才解决问题。这个教训让我意识到,机试环境下尽量用循环而不是递归,不是因为递归慢,而是递归多一层调用栈就多一重出错风险,尤其在精神状态紧绷的时候。

第二个崩溃瞬间发生在错误记录那道题。我把“文件名取最后16位”理解成了“取路径中最后一个文件名的最后16位”,但这个理解其实是错的。原题的逻辑是:如果文件名超过16个字符,从左边截掉多余的,保留最后16个。我因为漏看了“如果”两个字,导致截取逻辑完全错误。这个案例说明,读题时一定要逐字读,特别是条件状语,漏一个字就是完全不同的题。

第三个崩溃瞬间最冤。我的代码逻辑全对,本地测试通过,但提交后一直报格式错误。排查了二十分钟才发现,我习惯性地在输出的行尾加了一个空格,而这个空格在判题系统里被视为错误字符。从那以后,我每次提交前都会检查输出代码里有没有多余空格,这也成了我后来带人备考时反复强调的点。

6. 关于华为机试备考,最后想说的几句话

如果你正在准备华为的校招机试或者OD机试,我的建议是别去盲目刷一堆偏题怪题,先把这类“字符串处理+哈希统计+边界条件”的经典题型吃透。2016年这套题里出现的三个考点,直到现在依然是华为机试的主流方向,只是场景换了个皮而已。刷题的时候,每道题都要做到能讲清楚“为什么这么解”——为什么用HashMap而不是数组、为什么先统计再过滤、为什么边界条件要单独处理。能讲清楚这些,才算是真正掌握了一道题,而不是背了一道题。

我自己后来带人练习时,最喜欢用的一个方法,是把同一道题用C和Java各写一遍。这个习惯不是为了炫技,而是因为C语言逼着你手动管理内存和字符串,能帮你把底层逻辑理解得更透;Java则让你把重点放在数据结构和API的熟练度上。两种语言各写一遍,你对题目的理解深度是翻倍的。备考的最后阶段,找一个和华为OJ类似的在线平台,卡着时间做一套完整的模拟题,训练手感和时间分配。机试这件事,准备得越接近实战,考场上就越不慌。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询