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

资讯详情

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

vivo校招笔试B卷复盘:嵌入式与C++编程题考点详解

vivo校招笔试B卷复盘:嵌入式与C++编程题考点详解 秋招那会儿我投了不少手机厂商的研发岗vivo是其中比较早开笔试的一家。当时投的是嵌入式/安卓底层方向收到的是“2020届校招在线编程笔试B卷”。说实话看到“B卷”这两个字第一反应是“为什么不是A卷”后来才知道A/B卷只是并行出的两套题难度和知识点分布基本对等主要是为了防止同场考生互相参考。真正让我印象深刻的是这套题的技术考察范围比预期要杂——不是单纯的刷LeetCode里面还掺了不少C/C内存、进程调度、甚至手机场景相关的逻辑题这就很“手机厂笔试”。这篇文章我打算把当时复盘B卷的完整过程写出来包括题目类型、解题思路、踩坑点、以及最后的经验总结。不管你是准备投vivo校招还是想去其他手机厂商做研发这套试卷的考察逻辑都值得照着过一遍。1. 笔试整体设计与考察范围拆解1.1 试卷结构不是只有算法题vivo这套B卷的在线笔试时间大概是一个半小时到两个小时后端统一平台答题支持C/C、Java这类主流语言。题目类型大致分三块不定项选择题、代码填空题、在线编程题。选择题覆盖操作系统、计算机网络、数据结构基础约20道左右代码填空2到3道分值不大但很考验功底真正的重头戏是3道在线编程题占总分比重很高而且难度是逐步抬升的。选择题里你会看到一些很有“手机厂特色”的知识点比如死锁产生的必要条件、Linux进程调度策略、ARM架构下栈指针的用途、TCP三次握手为什么不能是两次。这些题目本身不算偏但需要你对底层原理有真正的理解而不是靠背八股。我当时就卡在了一道“关于堆和栈区别”的多选题上选项里有个描述是“栈上内存由编译器自动分配和释放堆上内存由程序员手动分配”乍一看对但后半句在Java里就不完全成立这就是典型的“用C思维选Java选项”的坑。编程题则更贴近业务场景。原题我现在记得不太全但按类型归纳B卷基本绕不开字符串处理、动态规划/贪心、数组与排序这三类。别觉得老套手机系统里有大量这类需求比如短信内容解析、手写输入法的笔画匹配、后台进程的内存清理策略本质都能抽象成这些算法模型。1.2 考察逻辑为什么这样出题我复盘完这套卷子最大的感受是vivo笔试不是单纯想筛“刷题机器”它更看重“能不能把算法用到实际场景里”。举个例子B卷有一道题是关于“手机桌面图标排列”的大意是给定多个应用的使用频率和图标拖动次数让你设计一个重排方案让用户最常用的应用尽量靠前。这看起来是一道业务逻辑题但抽掉外壳核心其实是“按权重排序局部交换最少操作次数”的算法问题难度不大但非常考验审题能力。另外手机厂商的研发岗笔试通常会对内存和效率更敏感。因为手机端的运算资源和电量有限一段代码如果时间复杂度从O(n)恶化到O(n²)在桌面级CPU上可能只是慢几十毫秒在手机端就可能导致卡顿和发热。所以B卷的选择题和填空题会特别照顾C语言指针、数组越界、静态变量生命周期这些“容易出性能事故”的知识点。备考的时候如果没有这种“手机场景意识”很容易觉得题目风格飘忽不定。这套卷子还有一个特点它会把一些“隐藏分”放在代码规范上。比如代码填空部分编译器不会告诉你哪里写得丑但换行、命名、注释风格线下阅卷的面试官是能看出来的。尤其是代码填空经常留空在“条件判断的边界”上这本身就是考察你有没有写防御性代码的习惯。1.3 适合谁来参考如果你是准备走vivo 2021届及以后的校招或者是想投其他手机大厂华为、荣耀、小米、OPPO的嵌入式、安卓应用、系统底层岗位这套B卷的复习价值都挺高。因为它基本锚定了手机厂研发笔试的三大方向编程语言基础、数据结构与算法、系统底层常识。哪怕你和我一样最终没去成vivo后期复盘时把这套卷子吃透对其他厂商的笔试也很有帮助。2. 核心题型解析与答题要点2.1 选择题考的其实是“概念的边界”B卷的选择题里我印象最深的一道是问“关于Android Binder机制下列说法错误的是”。这个知识点对日常开发手机App的人来说可能有点偏但对做系统层的岗位来说属于必须掌握的。选项有一个是“Binder通信需要经过内核态拷贝”实际上Binder使用了内存映射只需要一次拷贝所以“每个消息都要两次拷贝”才是错的。这种细节如果不深挖原理光靠刷选择题题库是很容易选错的。另一个高频考点是Linux的文件权限和进程管理。比如“查看系统当前所有进程”用什么命令——ps -ef、ps aux都行但如果你选成top严格来说它只是动态显示资源占用最高的进程不能显示全部。这种题就非常“实战化”要求你真的在Linux环境里敲过命令知道每个命令的长短版区别。我的答题习惯是遇到拿不准的多选题宁可少选不多选。校招笔试的计分规则通常是“选多不得分选少得部分分”所以别为了赌一个不确定的选项把自己拖下水。这一条在vivo B卷的选择题里尤其重要因为多选题占比不低。2.2 代码填空题三分写码七分看边界代码填空题在B卷里一般是2道给出一段带有空白的函数让你把空缺的表达式或语句补全。这种题比编程题更“阴险”因为上下文逻辑都给你了但留空的位置往往是整个函数的精髓所在——比如二分查找的mid更新、链表反转的指针暂存、字符串拷贝结束符的处理。B卷有一道填空题是让补全一个“从字符串中提取手机号码”的函数规则是提取连续11位以1开头的数字。代码框架已经写好了循环和isdigit判断唯一留空的地方是“检测到完整号码后如何移动指针继续扫描”。很多考生在这里习惯性写i但正确答案应该是i 11因为这样可以跳过已提取的号码段避免重复匹配。其实逻辑不难但如果你平时写字符串处理代码不注重“指针滑动”的细节就很容易在这种填空题上丢分。做代码填空题时我还习惯先看一眼函数开头有没有头文件依赖、结尾有没有return语句这样能猜出空缺处需要的是表达式还是完整语句。另外代码填空给的代码风格通常比较老旧缩进和命名可能不太规范别被带偏你自己填空的内容保持简洁、清晰就行。2.3 编程题从题意到代码的翻译能力B卷的3道编程题难度大约等于LeetCode的medium偏下但题面描述很长夹杂了很多业务背景。第一道通常是简单题比如“给定一个字符串统计其中每个字符出现的次数按次数从高到低输出”。这题本质上就是哈希表加排序但输出格式有讲究——要求“次数相同的按字典序排列”很多人排序时忘了这个次级条件直接导致部分测试用例过不了。第二道题开始上一点强度我记得是“给定一个整数数组找出其中连续子数组的最大和减去最小值的差最大是多少”。这题一眼看上去是动态规划但仔细分析后发现它其实是“滑动窗口单调队列”的最经典应用。如果不熟悉单调队列硬用暴力解法复杂度会是O(n²)在线评测的数据量一大就容易超时。当时我在这题上纠结了很久最后用了一个“前缀最小值和后缀最大值”的预处理做出来了空间换时间代码反而更简单。第三道压轴题一般是综合性较强的问题。B卷这道我记得是“有n个任务每个任务有优先级和耗时要求在总耗时不超过限制的情况下使得完成的优先级之和最大”。这就是典型的0/1背包变种只不过背包容积换成了“时间上限”每个物品的价值是“优先级”。如果你对背包问题比较熟改两行就能套用模板但如果只是背过代码而不理解dp数组的状态转移含义题目换层皮就认不出来了。3. 实操过程与核心环节实现3.1 环境准备与输入输出细节这里必须强调一下在线编程笔试的环境差异。vivo当时用的在线平台本地IDE顺手写的代码不一定能直接跑通主要原因是输入输出格式和OJ平台有差异。比如有些题目要求“多组输入”你需要用while(cin n)或while(scanf(%d, n) ! EOF)来循环读入如果不加这个循环本地测试单组数据是OK的在线评测就只通过一部分用例。另外要注意的是代码模板。如果你选C主函数签名是int main()最后记得return 0选Java的话类名必须是Main不能是其他名字。这些平台硬性要求看似琐碎但真有一批考生因为类名写成Solution或test直接编译失败。我当时在预演阶段就先建了个测试工程把几套常用的输入输出模板存成代码片段考试时直接复用省下不少时间。B卷的输入场景里字符串处理尤其要注意空格。比如第一题统计字符出现次数输入是一行可能含空格的字符串你用cin s读入的话只能读到空格前的部分。这时候要用getline(cin, s)或者C语言里的fgets。这个细节在本地IDE上可能不容易暴露但平台上多组输入一拼接问题就出来了。3.2 三种经典题型的参考实现先说字符串统计那题。我当时的代码思路是用一个mapchar, int记录每个字符出现的次数然后转成vector进行排序。排序规则先按次数降序次数相同再按字符升序。核心代码大概是这样的#include iostream #include map #include vector #include algorithm using namespace std; int main() { string s; getline(cin, s); mapchar, int cnt; for (char c : s) { cnt[c]; } vectorpairchar, int arr(cnt.begin(), cnt.end()); sort(arr.begin(), arr.end(), [](const pairchar, int a, const pairchar, int b) { if (a.second ! b.second) return a.second b.second; return a.first b.first; }); for (auto p : arr) { cout p.first : p.second endl; } return 0; }这里有个小坑是map默认按键排序所以遍历map时字符已经是字典序了但想要“次数降序、次数相同字典序升序”还是得手动写比较函数。另外题目如果要求输出格式是“字符:次数”记得别把冒号写成别的符号。第二道题我用的“前缀最小值”思路代码可以这样写#include iostream #include vector #include algorithm using namespace std; int main() { int n; while (cin n) { vectorint nums(n); for (int i 0; i n; i) cin nums[i]; vectorint prefixMin(n), suffixMax(n); prefixMin[0] nums[0]; for (int i 1; i n; i) { prefixMin[i] min(prefixMin[i - 1], nums[i]); } suffixMax[n - 1] nums[n - 1]; for (int i n - 2; i 0; i--) { suffixMax[i] max(suffixMax[i 1], nums[i]); } int ans 0; for (int i 0; i n - 1; i) { ans max(ans, suffixMax[i 1] - prefixMin[i]); } cout ans endl; } return 0; }这题为什么可以这样解因为我们要找的“连续子数组”实际上可以拆成两段最大值出现在右半段最小值出现在左半段而且两个半段要能够拼成一个连续整体。我遍历每个分割点左半段取该点以前的最小值右半段取该点以后的最大值两者的差就是跨越该点的最优解。全局取最大即可。这个思路比直接上单调队列好理解很多也不容易写错边界。第三道背包题标准写法如下#include iostream #include vector #include algorithm using namespace std; int main() { int n, limit; cin n limit; vectorint cost(n), value(n); for (int i 0; i n; i) cin cost[i]; for (int i 0; i n; i) cin value[i]; vectorint dp(limit 1, 0); for (int i 0; i n; i) { for (int j limit; j cost[i]; j--) { dp[j] max(dp[j], dp[j - cost[i]] value[i]); } } cout dp[limit] endl; return 0; }注意内层循环要从大到小这是0/1背包和完全背包的关键区别。如果从前往后遍历同一个任务就会被重复选择多次违反了“每个任务最多做一次”的约束。我当时就是因为顺手写成了正向遍历导致一组的样例答案是错的排查了半天才发现是循环方向的问题。这种“小细节毁大题目”的体验在线笔试里真的太常见了。3.3 实测踩坑记录在线编程笔试最让人抓狂的不是题目难而是“本地能过平台报错”。我在这套B卷里遇到过三个印象很深的坑第一个是数组越界。本地IDE我当时用的Visual Studio对越界读写的容忍度有时很高但Linux下的g编译器在特定优化级别下会直接报“runtime error”。我在第二道题里前缀最小值数组处理i-1时没有判断i0的边界本地测试用例刚好避开了到了平台就崩了。从那以后我写循环必先检查上下边界这习惯一直带到了现在。第二个是整型溢出。B卷的背包题如果任务耗时的总和超过int的范围用int接收输入就会溢出。当时的题目没给极端数据但防止万一我后来在做同类题时涉及累加、累乘的变量都会优先考虑long long。别觉得小题大做在线笔试的隐藏测试用例经常就是来“卡精度”的。第三个是输出格式不一致。有些题目要求“每个结果占一行”有些要求“结果之间用空格分隔”。我在第一道字符串题里没仔细看直接每个结果换行输出结果有几个测试用例格式错误。从那以后我总结了读题时先把Output部分圈出来看清楚分隔符和换行符再开始写代码。4. 常见问题与排查技巧实录4.1 选择题里的“最冤丢分点”我自己复盘时发现B卷选择题最容易丢分的地方不是知识点不会而是选项里的“绝对化表述”。比如“只要内存足够Linux系统可以同时运行无数个进程”——这个说法明显不对但如果你不细想很容易被“内存足够”这个前提带偏忽略进程数还受PID上限、文件描述符数、调度器开销等限制。还有“TCP连接可以同时传输多个文件”——传输层根本不管文件这层这属于把应用层的概念硬套到传输层上。应对这种题我的习惯是看到一个选项时先在脑子里问一句“有没有反例”。如果想到了一个反例这个选项大概率是错的如果想不出反例再结合其他选项做交叉排除。这个方法虽然土但在多选题里真的能救回不少分数。4.2 代码填空题的检查顺序代码填空容易在细节上翻车。我写完填空内容后会按下面这个顺序重新排查括号匹配补全的是括号表达式还是语句分号有没有加。变量名一致性空缺处使用的变量名和上下文里出现的存不存在拼写差异。数据类型int和float隐式转换时会不会丢失精度。边界条件循环变量从0还是从1开始结束条件带不带等号。返回值函数声明里写了返回值类型但所有分支是否都有return。这个顺序按照“编译 → 运行 → 逻辑”的层级来排优先级是从硬性错误到软性错误。一般来说在线笔试的代码填空空位不会太多只要按这个顺序过一遍基本能保住80%以上的正确率。4.3 编程题的排查技巧编程题如果提交后显示“答案错误”或“部分通过”我一般会做三件事第一造边界数据。例如数组长度为0数组长度为1所有元素相等已经有序倒序排列。这五种情况几乎能覆盖排序类题目80%的边界漏洞。第二写一个暴力解法用小数据量对拍。这个方法虽然费时间但在第二道题那种逻辑复杂的题里特别管用。我B卷时就是用暴力解法生成了几百组随机小数据和优化解法逐个比对最终发现是prefixMin数组的初始值设置错了。第三检查自己的输出是不是“多输出了调试信息”。这个听起来很蠢但我在紧张状态下真的干过——cout里存留了调试用的debug:前缀没删平台编译不报错但输出格式全错。后来我学乖了提交前全局搜索一遍cout语句确认每一条都是最终需要的输出。4.4 独家避坑心得最后说点更隐性的经验。第一不要死磕某一道题。B卷总共3道编程题如果你在第二道题上卡了半小时第三道题可能连题目都来不及看。我当时的策略是先把所有题通读一遍从最简单的开始做确保保底分拿到再回头啃难的。这一点在时间紧张时特别重要。第二善用注释来整理思路。在线编程IDE的调试能力通常比较弱不能断点调试。我习惯在写每个函数前先用注释把伪代码写出来比如// 1. 统计字符频率 // 2. 按频率和字典序排序 // 3. 输出结果这样就算思路中途断掉看一眼注释也能快速接上减少大脑重新加载上下文的成本。第三提前用目标OJ平台做模拟。vivo当时使用的在线笔试平台和我平时刷题的LeetCode、牛客网在提交和反馈方式上都有区别。有些平台不会实时显示“通过/不通过”只有交卷后才能知道分数这种“无反馈”环境非常考验一次性写对的能力。如果有条件最好提前找一个“无评测反馈”的OJ练习几道题适应一下这种不确定感。第四注意时间分配。选择题如果平均一道超过1.5分钟就说明你可能在某道题上陷进去了。校招笔试题量通常偏大偶尔一两道判断题/多选题不会做是很正常的先标记一下跳到后面才是理性的选择。我记得B卷结尾还有几道关于手机系统进程判断的选择题如果没有宏观节奏感很容易在单选上浪费太久导致最后编程题没时间完善。5. 复盘总结B卷对后续面试的启示很多人以为笔试结束就万事大吉其实笔试的反馈对后面的面试很有参考价值。我后来收到了vivo的面试通知面试环节里问到的一些技术点和B卷笔试里的知识块高度重合——比如Linux进程通信方式、Java内存区域、字符串匹配算法。也就是说笔试题目本身就是面试官给你画的重点。我当时的做法是笔试结束后立刻把回忆版题目整理成文档标出自己不确定的选项然后逐个去查资料把相关的知识点补齐。这件事帮了大忙因为面试官很喜欢在简历上看到“对底层原理有主动探索的热情”而你对笔试问题的复盘恰好能说明这一点。另外B卷里考到的“背包问题”和“滑动窗口”这类算法后来在其他手机厂商的笔试题中也出现过。算法题的知识点就那么几十个手机厂笔试尤其喜欢在“数组处理”和“字符串处理”这两个大锅饭里来回翻炒。如果你这轮能把B卷吃透其实相当于提前解锁了下一次笔试的副本。我个人还有一个小经验笔试复盘不要太在意分数。我当年B卷编程题第三道其实没完全跑通但最后还是拿到了面试机会因为面试官更看重你在代码里展现的思维清晰度以及面对没做出来的题时你会怎么补漏。只要你把思路写出来哪怕没跑通也比留白要强得多。所以在线编程笔试里就算不会做也要把暴力解法的代码写上把注释写上说不定这部分就给面试官留下了不错的印象。vivo 2020届校招B卷这套题目放在今天来看依然是手机厂商校招笔试的一个典型切片。它不算特别难但真的非常考验“把基础知识用出来的能力”。如果你正在准备类似岗位的笔试我建议别只盯着LeetCode刷题多回头看看操作系统、计算机网络和C/Java的内存模型这些“老知识”才是手机厂笔试的真正分水岭。最后再分享一个小技巧笔试前务必确认自己习惯了“把脑袋里的伪代码一口气写成可运行的代码”。在线编程平台不像本地IDE那样可以随写随调试一旦进入考试环境每一分钟都很珍贵。平时练习时刻意训练自己在15分钟内把一道medium题完整敲出来不依赖自动补全不比对着题解写。等你真正上了vivo的B卷考场会发现这套训练比刷十套模拟题都管用。
返回列表