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

资讯详情

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

vivo秋招软件岗笔试复盘:题型考点与编程题实战解析

vivo秋招软件岗笔试复盘:题型考点与编程题实战解析 2023年vivo秋招软件岗第一批笔试我在交卷前5分钟把一道动态规划改成了贪心没想到居然过了。这里把整场笔试的题型、考点、踩坑点原原本本复盘一遍给后面准备手机厂商软件岗的同学一些参考。先说结论vivo笔试整体难度在主流厂商里算中档偏上选择题覆盖面很广但不算偏重点在计算机网络、操作系统、C/Java语言特性和数据库编程题三道没有纯模板题每一道都需要一定的思维转换而不是背板子就能秒。1. 整体情况与题型结构分析1.1 考试形式与时间分配vivo秋招软件岗笔试用的是牛客网平台限时90分钟总共大概28道题题型分三块单选题约15道、多选题约7道、编程题3道。总分100分其中选择题占50分编程题占50分分值基本对半开也就是说编程题和选择题同样重要不存在“选择题随便答编程题写出来就行”的说法。时间分配上我建议选择题控制在40分钟以内给三道编程题留至少50分钟。但实际操作下来单选题里藏着不少需要动笔算的题后面会详细说一不留神就会超时所以做选择题时一定要有“取舍感”想超过两分钟还没思路的题先标记跳过不要恋战。我这次就是在一道C内存对齐的多选题上卡了五分钟导致后面编程题时间被压缩第二道题差点没写完。牛客网笔试页面支持本地IDE写代码然后粘贴提交但考试过程中不能切换浏览器标签页或使用本地编译器全程监测浏览器行为。所以平时练习就尽量习惯在网页编辑器里写代码不要依赖IDE的代码提示否则笔试时会很不适应。1.2 考察范围权重分析从我个人回忆和同批笔试群里讨论的情况来看vivo软件岗笔试的知识点覆盖面大致呈现这样的权重分布模块占比估算高频考点计算机网络20%-25%TCP三次握手、拥塞控制、HTTP状态码、DNS解析操作系统15%-20%进程线程区别、死锁条件、页面置换算法、进程调度C/Java语言20%-25%虚函数、内存对齐、智能指针、JVM内存结构、并发包数据库10%-15%索引结构、事务隔离级别、B树、SQL语法智力题/数学5%-10%概率计算、排列组合、逻辑推理数据结构与算法10%左右复杂度和代码题相关基础概念这个分布基本代表了手机厂商软件岗的技术考察偏好操作系统和计算机网络是重点因为它们直接关系到底层系统优化和通信协议理解语言特性侧重内存管理相关知识点这和C/C开发方向的岗位需求强相关。一个值得注意的细节是vivo的笔试题目明显比互联网大厂更“扎实”没有那种靠猜的脑筋急转弯题大部分选择题都是实打实的课本知识结合实战场景。比如TCP拥塞控制那道题选项不是简单背“慢开始、拥塞避免、快重传、快恢复”的顺序而是给出一个具体的cwnd变化序列让判断处于哪个阶段对知识点的理解深度要求更高。2. 选择题核心考点详解与避坑建议2.1 计算机网络协议细节是分水岭vivo笔试的计算机网络题难度比很多互联网公司高一个档次。我这里印象最深的一道题是TCP拥塞控制的综合分析题目描述一个TCP连接从建立开始ssthresh设为16当cwnd增长到多少时进入拥塞避免阶段发生超时后ssthresh和cwnd分别变成多少这题光背“慢开始指数增长拥塞避免线性增长”是不够的你得知道具体是cwnd超过ssthresh后才进入拥塞避免而且必须注意慢开始阶段cwnd从1开始每轮RTT翻倍但翻倍是在收到全部ACK之后不是发送之后立即翻倍。我第一次做这种题也经常算错后来总结出一个口诀计数翻倍再判断。一轮传输结束后先把cwnd的值与ssthresh比较决定下一轮按什么算法增长。HTTP状态码那道题难度不大问的是301、302、304、404分别代表什么以及浏览器在什么场景下会缓存重定向结果。这题的坑在于304和307容易混淆304表示内容未修改服务器告诉浏览器可以用缓存307表示临时重定向且保持请求方法不变。我记得当时还出了一道DNS解析流程的选择问的是浏览器访问一个域名时DNS查询的先后顺序是“浏览器缓存-系统缓存-路由器缓存-本地DNS服务器”。考点不偏但需要对整个链路有清晰认知。2.2 操作系统死锁和页面置换是常客操作系统板块vivo对死锁理论的考察很有意思。题目没有直接问“死锁的四个必要条件是什么”而是给出一个具体的资源分配场景两个进程各自持有一个资源又同时请求对方持有的资源问这属于死锁的哪个必要条件中的哪个场景。这就是典型的“知识点变形”如果你只是背了四个条件的名字不知道“保持并等待”“不可剥夺”“循环等待”如何在具体场景中体现就很容易选错。页面置换算法也考了一道LRU给了一个访问序列问物理块为4时的缺页次数。这类题没有捷径必须动手画表一个访问一个访问地推。我建议平时练习时就养成用表格推演的习惯这样考场上能快速准确算出结果。计算完建议把结果代入检查一遍因为一位数字之差就会导致完全不同的答案。进程和线程的辨析题比较基础考的是线程共享哪些资源、独立哪些资源。这题的经典陷阱是“线程共享进程的地址空间但有自己的栈和寄存器”部分选项会故意把栈说成共享的看题要细。2.3 语言特性内存细节决定成败vivo笔试混考了C和Java的内容这也符合软件岗统一笔试的风格考生需要按自己擅长的语言方向作答但对方语言的基础也不能完全不懂。C部分我遇到的内存对齐计算题值得单独说。题目给了一个结构体struct Test { char a; // 1字节 int b; // 4字节 short c; // 2字节 };问 sizeof(Test) 是多少并给出各成员偏移量。这题的答案是12不是简单的1427。计算过程是char a占偏移0int b需要4字节对齐所以b从偏移4开始中间空出3个字节short c需要2字节对齐从偏移8开始占2字节到偏移9结构体总大小需要是最大对齐数这里是4的整数倍所以是12。我当时在这道题上浪费了不少时间因为选项里还有sizeof在默认对齐和#pragma pack(1)下的两种结果。建议准备笔试时把“T内存对齐、pragma pack、offsetof宏”这几个知识点串起来复习。C虚函数表的题也考了一道问的是“一个类有多个虚函数请问该类的对象内存中存储的虚函数表指针有几个”。正确答案是1个因为同一张虚函数表里按声明顺序排列所有虚函数地址不是每个虚函数一个指针。这和Java的虚方法分派思路类似很多同学容易踩坑。Java板块的题集中在JVM内存结构和并发包。JVM那道题问的是“JDK8中以下哪个区域在OOM异常时抛出 OutOfMemoryError: Metaspace”这个其实就是本地内存区域里的元空间。Java并发题考的是ConcurrentHashMap在JDK7和JDK8中实现的关键差异JDK7用分段锁SegmentJDK8用CASsynchronized锁Node头节点。这类题靠突击无效需要平常积累。2.4 数据库与逻辑题数据库题相对友好B树的特征、事务隔离级别和脏读/幻读的关系是必考内容。我遇到的是InnoDB存储引擎里RR可重复读隔离级别能否解决幻读的问题答通过MVCC间隙锁Gap Lock可以在大多数场景下避免幻读但在某些边界条件下仍可能出现幻读比如条件列没走索引时间隙锁退化成表锁还是会有并发风险。这个细节如果只是背“RR隔离级别解决了幻读”这道题就挂了。逻辑题只考了一道概率两个人轮流抛硬币先抛到正面的人获胜问先手获胜的概率。答案是2/3很多人写的是1/2忽略了“第一轮先手没有抛出正面后手抛出正面”这类多轮情况。求解思路是设先手获胜概率为P第一轮赢的概率是1/2如果第一轮双方都没赢概率1/4则回到初始状态所以P1/21/4×P解得P2/3。3. 编程题实战场三道题的完整解题复盘3.1 第一题滑动窗口变种简单但容易踩坑第一道编程题难度不大题目大意是给定一个整数数组和一个目标值k输出数组中所有长度为k的连续子数组的平均值中的最大值。这题的核心考点是滑动窗口而不是每个窗口单独求和。第一次做这类题的同学容易写出暴力解法每到一个位置内层循环遍历k个数求和时间复杂度O(n×k)。当数组长度到达10^5级别、k也很大时直接就超时了。正确解法是维护一个窗口和每次窗口右移一格减掉离开窗口的元素加上新进入窗口的元素把每次窗口的均值记录下来取最大#include vector #include algorithm using namespace std; double maxAverage(vectorint nums, int k) { int n nums.size(); if (n k) return 0.0; long long sum 0; for (int i 0; i k; i) sum nums[i]; long long maxSum sum; for (int i k; i n; i) { sum nums[i] - nums[i - k]; maxSum max(maxSum, sum); } return (double)maxSum / k; }这里有个小坑是必须用long long保存窗口和因为元素可能很大int溢出会导致答案错误。vivo的笔试平台用的是牛客自带的判题系统只给错误样例的几个简单用例不会跑大数据集所以这种情况很可能交上去显示AC但实际上没有AC真实情况会因为你没写long long 导致部分测试用例过不去。我在代码里直接用long long规避了这个风险。第二坑是double的精度问题求均值时直接除k就行不要用“加上每个元素除以k的值”这种公式浮点数累加误差会变大。3.2 第二题贪心场景题读完题要想清楚模型第二题是一道游戏场景题大意是有一个技能可以造成范围伤害给定怪物血量数组和每个怪物的位置坐标每次可以选择一个位置释放技能对该位置左右范围L内的所有怪物造成一次伤害问最少需要释放多少次技能才能杀死所有怪物。这道题实际上是一个区间覆盖问题但套了一层游戏皮。很多人读完第一反应是动态规划或二分答案其实贪心就能解决对于最左边还没被覆盖的怪物最优策略一定是在“能覆盖到这个怪物的前提下技能释放位置尽量靠右”这样能覆盖到更多右边的怪物。#include vector #include algorithm using namespace std; int minSkills(vectorint positions, vectorint hp, int L) { int n positions.size(); vectorpairint, int monsters; for (int i 0; i n; i) { monsters.push_back({positions[i], hp[i]}); } sort(monsters.begin(), monsters.end()); int res 0; vectorint cur; for (int i 0; i n; ) { if (cur[i] 0) { i; continue; } // 技能释放位置的右边界 int rightBound monsters[i].first L; int idx i; while (idx n monsters[idx].first rightBound) { idx; } idx--; // 对 [i, idx] 范围内的怪物造成一次伤害 for (int j i; j idx; j) { hp[j] - 1; } res; } return res; }这个暴力写法时间复杂度过高正确的做法是记录当前技能覆盖到的最右位置用一个变量维护已经造成的累计伤害每个怪物血量减去累计伤害后若仍有剩余则新增技能并把覆盖边界右移。这道题让我最难受的地方是思路转换。当时我先写了一个区间DP的解法写了半天发现状态转移特别复杂后来冷静下来才发现是贪心。写代码前如果能在草稿纸上做几个小样例推演比如只有三个怪物、L很小的边界情况很快就能看出贪心策略是对的代码量也会小很多。3.3 第三题树形DP与哈希结合区分度最高第三题是三道题里最难的也是整个笔试区分度最高的一道题目大意是给定一棵树每个节点有一个颜色值问从根节点到每个子树中出现次数最多的颜色是哪种若多个颜色出现次数相同输出颜色值最小的那个。看到这题我的第一反应是树上启发式合并DSU on Tree因为它完美匹配“统计子树中出现次数最多的颜色”这个场景。DSU on Tree的核心思想可以这样理解统计一个节点的子树信息时先暴力处理所有轻儿子的子树再处理重儿子保留重儿子的信息不删除这样每次重儿子的结果可以直接复用把复杂度从O(n²)降为O(n log n)。简单来说分三步第一步求出每个节点的重儿子。重儿子就是子树节点数最多的那个子节点类似树链剖分里的定义。第二步DFS遍历时对每个节点先递归处理所有轻儿子处理完一个轻儿子就把它的贡献从计数数组中删除然后递归处理重儿子重儿子的贡献保留在数组里。第三步把当前节点自己和所有轻子树的贡献加入计数数组然后更新答案。核心代码结构大概是void dfs(int u, int fa, bool keep) { // 先处理轻儿子 for (int v : children[u]) { if (v fa || v heavySon[u]) continue; dfs(v, u, false); } // 再处理重儿子保留状态 if (heavySon[u] ! -1) { dfs(heavySon[u], u, true); } // 加入轻儿子和当前节点的贡献 add(u, fa, false); cnt[color[u]]; if (cnt[color[u]] maxCnt || (cnt[color[u]] maxCnt color[u] ansColor)) { maxCnt cnt[color[u]]; ansColor color[u]; } // 加回所有轻子树贡献 for (int v : children[u]) { if (v fa || v heavySon[u]) continue; addSubtree(v, u); } ans[u] ansColor; // 如果 keep 为 false清空贡献 if (!keep) { removeSubtree(u, fa); } }这段只是结构示意实际代码里add和remove函数必须逐节点遍历子树更新数组。我就是把“先加轻儿子再加重儿子贡献”这个顺序搞反了导致统计结果出现偏差调试了二十分钟才定位到问题。所以第二题做完后我看到时间只剩不到二十分钟心里是很慌的这道题完全是靠平时背过的模板撑下来的。后来笔试群里不少同学反馈这道题连题面都没读懂或者只写了暴力遍历每个子树统计O(n²)的解法拿到部分分可见vivo把区分题放在最后一题就是用来卡大厂的“编程题熟练工”的。3.4 编程题的整体策略总结结合三道编程题我的建议是第一题一般是滑动窗口、双指针或者简单模拟10分钟内必须AC第二题是贪心或二分答案20分钟内必须AC第三题是树、图或动态规划综合题给到20到25分钟尝试用最熟练的模板解决。如果第三题完全没思路把暴力解法写上去也能拿分。vivo的编程题是按测试用例百分比给分的部分AC比完全空着强很多。但这个“部分分”平时刷题很难练因为LeetCode等平台不允许你提交非最优解。4. 备考策略与实战经验总结4.1 时间分配与刷题方向回忆完整场笔试我认为vivo软件岗笔试对候选人的要求集中在三个方面基础扎实选择题不丢分、模板熟练编程题前两道快速AC、思维深度第三题有思路。选择题部分建议重点复习计算机网络TCP拥塞控制、TCP/UDP区别、HTTP/HTTPS通信过程、DNS操作系统死锁、页面置换、进程调度、内存管理、线程模型语言特性C虚函数和内存布局、智能指针、Java JVM和集合源码数据库索引原理、事务隔离级别、SQL优化编程题方面vivo比较喜欢考滑动窗口、贪心、前缀和、树结构遍历、图的最短路径这五类。刷题时不要盲目追求难题把常见模板吃透更重要。我个人的经验是LeetCode Hot 100里的中等题刷两遍再加一些树形DP和区间DP的专项训练应付这类厂商笔试完全够用。4.2 我在考场上的几个失误复盘过程中我发现了自己的三个失误写出来给大家提个醒。第一个失误是多选题中C内存对齐那道题耗时过长导致编程题时间被压缩。多选题分值高如果选错一个选项就全扣所以遇到需要计算的题如果估算超过三分钟果断先标记做完其他题再回头算。千万别在一道2分的题上丢掉后面20分的编程题时间。第二个失误是编程题第二题写完后没有充分测试边界情况。好在这道题我用的贪心思路本身是对的但如果有大量怪物血量相同的情况代码里累计伤害的逻辑会覆盖不到导致部分用例超时。以后写题还是要留两分钟在草稿纸上构造几个边界用例比如“所有怪物血量都是1”“k等于数组长度”“数组只有一个元素”等再交卷。第三个失误是第三道树形DP没有在写代码前确认“多个颜色出现次数相同输出颜色值最小”这个比较逻辑。如果当时想清楚了这个细节代码里的比较条件是可以抽出成一个小函数就不会因为粗心写错。这个如果在核心代码段的if条件里出错调试起来极其浪费时间。4.3 关于笔试的几句真心话面完vivo这批笔试之后我最大的一个感受是手机厂商软件岗的笔试风格比互联网大厂更“学院派”对基本功的考察非常扎实不像有些公司刷一套题就能过而是需要长期积累。这种笔试形式决定了突击复习效果有限真正能拉开差距的是操作系统、计算机网络这些计算机基础课程学得扎不扎实以及日常刷题时有没有形成自己的模板库。备战的最后一周与其刷一百道新题不如把之前做过的高频模板题重新敲一遍。我考前就把滑动窗口、二分答案、DSU on Tree、并查集、快速幂这些模板手敲了三遍敲到不用思考就能写出来。事实证明考场上的紧张状态下只有形成肌肉记忆的东西才写得出来。如果你马上要参加vivo或同类型公司的笔试再多说一个细节牛客网写代码时读入一定要用快速IOC就用scanf/printf或关闭同步的cin/cout不要直接cin/cout裸奔数据量一大会超时。这种低级失误最可惜一定要提前规避。
返回列表