尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

华为2016研发工程师编程题真题复盘:三道经典题与核心考点全解析

华为2016研发工程师编程题真题复盘:三道经典题与核心考点全解析 2016年秋天我参加华为研发岗校招笔试时的那个场景到现在还记得很清楚。三道编程题两个小时的在线OJ考试全屏监控期间不能切出页面第二题我改到最后一分钟才提交交完手心全是汗。后来这几年我陆续帮学弟学妹辅导华为的机试又回头把2016年那套题翻出来重做了一遍才发现这批题出得相当有水平——表面上看都是基础题实际上每一道都在筛工程思维而不是筛刷题数量。这篇文章我就把华为2016研发工程师编程题的考察逻辑、三道经典真题复盘、核心考点拆解和踩坑经验完整梳理一遍正要投华为研发岗、或者准备华为OD机试的同学可以拿来当一份备赛参考。1. 2016年华为研发岗机试到底在考什么先说整体情况。2016年华为研发工程师校招在线笔试常规配置是三道编程题时间两个小时平台是华为自研的OJ系统支持C/C/Java等主流语言。题量不大但时间也不算宽裕尤其是第三题如果在字符串处理上卡住后面基本没有回头检查的机会。从题目风格上看三道题分别覆盖了字符串处理、数学模拟、数据统计三个方向没有图论没有复杂的动态规划也没有需要背诵模板的高级数据结构。当时很多人觉得华为笔试简单无非就是考字符串、数组、循环。这个说法对了一半。题目本身确实不偏不怪但它和你平时在LeetCode上刷的Medium题完全是两套逻辑。华为的题更贴近工程场景处理日志里的错误记录、解析输入的格式、把物理世界的问题转化成代码逻辑。它考的不是你背了多少算法模板而是你能不能把一个具体问题拆成计算机能执行的步骤并且把边界条件处理干净。还有一个容易被忽略的点华为的OJ判题是按测试用例给分的通过了多少用例就得多少分不是一锤子买卖看是否AC。这意味着你哪怕只写出了暴力解法只要能把简单用例跑过去就能拿到一部分分数。这个设计其实非常工程化——真实项目里先跑通再优化永远比憋一个大招强。所以策略上遇到卡壳的题先把暴力版写出来拿分比死磕最优解明智得多。再说说备考方向。2016年的题目风格在之后的华为校招和OD机试里延续了很久尤其是“字符串统计边界条件”这个组合几乎是每场必考。后面有一年出的“简单错误记录”和2016年的第三题在考点上一脉相承。所以这份真题复盘不只是回望一份老卷子它对当下备考华为机试的人仍然有直接的参考价值。2. 三道经典真题复盘代码与思路全解2.1 第一题删除字符串中出现次数最少的字符这道题在很多华为机试经验帖里都出现过属于典型的“送分但不完全送分”题。题目要求是输入一个字符串统计每个字符出现的次数找出出现次数最少的字符把这些字符从原字符串中删除输出删除后的结果。如果多个字符出现次数一样且都是最少则把它们全部删除。输出时保留原字符串中其他字符的相对顺序。举个例子输入abcdda、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码做下标这样大小写、数字、特殊字符都能覆盖。第二个容易错的地方是“出现次数最少的字符”的定义。比如字符串是aabbca和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); MapString, 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.EntryString, 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 三个我亲历的崩溃瞬间第一个崩溃瞬间发生在汽水瓶那道题。我一开始写的是递归版本逻辑是对了但递归函数里忘了写终止条件导致到了n1的时候还在递归直接栈溢出。后来改成循环版才解决问题。这个教训让我意识到机试环境下尽量用循环而不是递归不是因为递归慢而是递归多一层调用栈就多一重出错风险尤其在精神状态紧绷的时候。第二个崩溃瞬间发生在错误记录那道题。我把“文件名取最后16位”理解成了“取路径中最后一个文件名的最后16位”但这个理解其实是错的。原题的逻辑是如果文件名超过16个字符从左边截掉多余的保留最后16个。我因为漏看了“如果”两个字导致截取逻辑完全错误。这个案例说明读题时一定要逐字读特别是条件状语漏一个字就是完全不同的题。第三个崩溃瞬间最冤。我的代码逻辑全对本地测试通过但提交后一直报格式错误。排查了二十分钟才发现我习惯性地在输出的行尾加了一个空格而这个空格在判题系统里被视为错误字符。从那以后我每次提交前都会检查输出代码里有没有多余空格这也成了我后来带人备考时反复强调的点。6. 关于华为机试备考最后想说的几句话如果你正在准备华为的校招机试或者OD机试我的建议是别去盲目刷一堆偏题怪题先把这类“字符串处理哈希统计边界条件”的经典题型吃透。2016年这套题里出现的三个考点直到现在依然是华为机试的主流方向只是场景换了个皮而已。刷题的时候每道题都要做到能讲清楚“为什么这么解”——为什么用HashMap而不是数组、为什么先统计再过滤、为什么边界条件要单独处理。能讲清楚这些才算是真正掌握了一道题而不是背了一道题。我自己后来带人练习时最喜欢用的一个方法是把同一道题用C和Java各写一遍。这个习惯不是为了炫技而是因为C语言逼着你手动管理内存和字符串能帮你把底层逻辑理解得更透Java则让你把重点放在数据结构和API的熟练度上。两种语言各写一遍你对题目的理解深度是翻倍的。备考的最后阶段找一个和华为OJ类似的在线平台卡着时间做一套完整的模拟题训练手感和时间分配。机试这件事准备得越接近实战考场上就越不慌。
返回列表