每年到了八九月份,校招笔试就像潮水一样涌过来。前几天整理资料的时候翻到自己存的一份“触宝科技2017秋季校招研发笔试题(第一批)”,突然感慨挺多的。触宝这家公司可能很多人第一反应是输入法,但它的业务线其实很杂,输入法、通讯录、健康助手都做过,而且海外市场做得比国内名声还响。当年这批研发笔试题放到现在来看,依然很有参考价值,因为它考的不是冷门屠龙技,而是研发岗最核心的那几板斧:编程基础、算法思维、系统知识,再加上一些临场处理问题的能力。今天干脆把这套题的整体风格和具体考点拆开聊聊,给正在准备校招或者想跳槽进移动互联网公司的朋友做个参考。
2017年的校招题和现在相比,形式上区别不大,仍然是“选择题+编程题+问答题”的老三样,但内容上更看重基础功底的扎实程度。那批题我印象最深的地方在于,它并不追求题目有多难多偏,而是把很多基础知识点换了个场景去考,看似眼熟,动起手来却发现有不少坑。这篇文章不会原封不动地把题目贴出来,那样意义不大,我会把同类型的考点、题型逻辑、容易踩的坑和对应的解题思路完整梳理一遍,让你即使拿不到原题,也能在笔试现场心里有底。
1. 笔试题整体风格与考察逻辑
1.1 研发笔试题的三层结构:基础、逻辑、实战
很多第一次参加校招的同学拿到触宝这类公司的笔试题,第一反应是“怎么这么杂”。其实杂是正常的,研发岗笔试本质上不是考你某一门课学得多精,而是考你在有限时间内能不能稳定输出基础知识、快速理解问题并写出可用代码。我当时看到的这套题可以清晰分成三个层次。
第一层是基础知识题,覆盖了数据结构、操作系统、计算机网络、数据库。这些题目看着像期末考试选择题,但实际风格更偏向“用场景考概念”。比如给你一段代码问输出多少,或者在多个网络异常场景里选出TCP会如何处理,还会给一个SQL表结构让你挑出查询语句中的错误。这些题考的并不是背诵,而是你真正有没有理解底层机制。
第二层是逻辑和算法题,一般是一两个选择题加一道到两道编程题。编程题往往不是LeetCode原题,而是给你一个业务背景,然后让你实现某个功能。这种做法挺考验人,因为一旦题目包装成“用户输入一段文本需要做统计”,你可能半天反应不过来它其实可以简化为哈希表计数问题。
第三层是综合问答题,通常会涉及系统设计、并发、内存管理或者让你阐述某个模块的实现思路。比如“如果一个功能在低性能手机上崩溃,你会怎么排查”“设计一个防止重复点击的装饰器/组件”这类实际问题。这层题目没有绝对标准答案,考官想看的是你的分析思路和知识广度。
这三层结构其实是国内研发校招笔试的典型配置。触宝作为移动互联网公司,特别看重你是否有端侧或者服务端开发的潜质,所以题目会明显偏向Android/iOS底层、网络通信、数据存储这些方向。但好消息是,它不会上来就考你某某框架的源码,还是以计算机基础为根。
1.2 为什么很多同学觉得题量不大却做不完
触宝这批笔试我记得限时大概是90分钟到120分钟,题量看年份不一样,第一批大概有20道左右的选择题加两道编程题加一两道问答题。很多同学考完跟我抱怨:题目看着都会,怎么就是做不完?答案往往出在时间分配上。
第一,选择题里暗藏了大量“变体题”。比如一道关于进程线程的题,表面问“以下哪些是线程私有资源”,选项中可能混了“文件描述符”“栈”“堆”“寄存器”。如果你只记得“线程共享进程资源”,就会掉进坑里,多纠结一会儿,时间就过去了。这类题往往不是1分钟能解决的,它需要你在脑子里做一遍完整推演。
第二,编程题虽然输入输出格式很常规,但测试用例比较刁钻。如果只写核心逻辑不考虑边界,可能在示例用例上能跑通,但一提交就Fail。很多同学为了一道题反复试探、查错,白白耗掉半小时。这在笔试环境中特别吃亏,因为笔试不给你编译器报错之外的任何反馈。
第三,很多人喜欢按题目顺序作答,遇到一道卡壳就死磕。笔试不是高考,它的题目难度并非严格递增,经常是前面选择题里有陷阱,后面编程题反而容易拿分。如果你前面耗太久,后面的题即使会也没时间写。
我自己当年参加类似笔试时,养成了一个习惯:拿到题先花两分钟浏览全卷,把选择题里明显简单和可能耗时的标记出来,编程题先选自己有思路的做,最后再回头啃硬骨头。这个习惯在面对触宝这类题量适中但思维量大的试卷时非常有效。
2. 经典题型的核心思路拆解
2.1 编程题:限制条件下的优化(以字符串压缩为例)
触宝这类公司笔试编程题不会太魔怔,一般能用一个数据结构加一个算法解决。这里我拿一道和2017年风格非常接近的题目举例:实现字符串的“压缩”功能。题面类似这样:
给定一个字符串,例如 "aaabbc",请将其压缩为 "a3b2c1"。如果压缩后的字符串长度不小于原字符串长度,则返回原字符串。要求在原字符串上修改,如果空间复杂度超过O(1)则不给分。
这道题很有代表性,因为它把三个考点揉在一起:字符串处理能力、对空间复杂度的敏感度、边界条件处理。
第一眼看上去,最直接的做法是新建一个新的字符串或列表,遍历原字符串并统计连续相同字符的数量,拼接结果。但在“原字符串上修改”和“空间复杂度O(1)”的限制下,这个做法不可行。如果笔试环境是C语言,你可以考虑双指针原地压缩;如果是Java或Python,字符串本身不可变,这时“原字符串上修改”通常是希望你在字符数组层面操作。
正确的思路分两步。第一步,先遍历一遍计算压缩后的总长度,如果长度不小于原串长度,直接返回原串(这个问题很简单)。第二步,用两个指针:一个读指针从头遍历,一个写指针指向当前覆盖位置。每遇到一个连续字符段,就在写指针处写入该字符,再写入该字符出现的次数(注意次数可能多于一位数字,需要把数字转字符串后逐个写入)。由于写指针永远不超前读指针,所以可以在字符数组上原地覆盖。最后在新的写指针位置补上字符串终止符即可。
为什么这道题容易卡壳?因为很多人只想到用哈希表统计字符总频次,把 "aaabbc" 压缩成 "a3b2c1" 确实没问题,但无法处理 "abbbbbbbbbb" 这样压缩后反而更长的场景,而且如果不计算最终长度就直接覆盖,可能读到已经被覆盖的字符。我当时在练习的时候,就犯过没考虑多位数次数的错误,比如字符连续出现12次,把'1'和'2'当两个字符写入,结果写指针计算错位,最终答案全乱。
这类题的通用启示是:做题先确定限制条件,再选数据结构。很多编程题考的不是你有没有思路,而是你能不能把思路在约束条件下落地。“先计算再执行”是原地修改类问题的经典手法,值得多练。
2.2 算法题:如何在紧张环境下快速定位最优解
触宝笔试题里的算法题很少是纯背板子的题,更多是把经典算法包装一下,看你能不能识别出底层的算法模型。举一个我印象很深的类型:在“循环有序数组”中查找目标值。
题面类似:一个原本升序排列的数组在某个未知点做了旋转,例如 [4,5,6,7,0,1,2],给定一个target,要求时间复杂度O(log n)查找目标值。
大家一看就知道这是LeetCode上Search in Rotated Sorted Array的变体。但笔试里不会告诉你“这是旋转数组”,而是给了一个业务背景,比如“某App的消息列表按时间倒序,但由于时区异常,部分数据发生错位,请设计高效查询算法”。需要你自己把表面问题抽象成已知模型。
抽象出来之后,解法就明确了:变形的二分查找。核心思路是在每次二分时,判断哪一半是单调有序的。由于旋转数组的两半中至少有一半是有序的,比较 mid 和 left 或 right 的值可以确定有序区间。如果 target 落在有序区间内,则继续在那个区间二分;否则到另一区间。关键在于相等的处理:如果 nums[left] == nums[mid] 且 target 不是这个值,比如数组里有重复元素,就需要 left++ 逐步缩小范围,防止死循环。
很多同学在笔试时容易忽略重复元素,因为示例里没给。但笔试真正跑测试用例时会用大量边界数据。比如 [1,1,1,2,1] 这种,target=2,如果代码里简单判断 nums[mid] < nums[right] 就右移,很可能误判。触宝这类题也是,考的就是你有没有严谨地考虑所有可能分支。
我在做题时的习惯是:先不急着写代码,在草稿纸上手写两个例子,一个正常一个带重复值,把二分的过程走一遍,再动手写。这个步骤花不到两分钟,但能帮你避免一半以上的逻辑漏洞。笔试时间虽紧,但这步省不了。
2.3 计算机网络与操作系统:高频易错点
除了编程题,触宝这类公司非常喜欢考TCP/IP和操作系统的题目,毕竟移动端应用跟网络和数据存储深度绑定。这一块选择题和问答题都很常见。
TCP的题目是重灾区。比如问“TCP三次握手中,第二次握手服务端发送的报文段中SYN和ACK标志位分别是什么”,正确答案是SYN=1, ACK=1。但很多人会误写成SYN=0, ACK=1,因为他们以为第二次握手是“同意连接”,所以只有ACK。实际上第二次握手既要同意客户端的连接请求,又要发起服务端到客户端的连接,所以两个标志位都是1。这种细节就是在考你有没有真正理解握手的双向性。
还有一道很经典的网络题:为什么TCP建立连接需要三次,而断开连接需要四次。很多人只知道答案,不知道背后的原因。建立连接需要三次是因为要同步双方的初始序列号,并确认双方都有收发能力;断开连接需要四次是因为TCP连接是全双工的,每一方的关闭都需要单独确认。如果只是死记硬背“SYN、SYN+ACK、ACK”和“FIN、ACK、FIN、ACK”,一换场景就容易露馅。
操作系统的考点则集中在进程线程、死锁、虚拟内存、并发控制。常见题目如“多线程程序里哪些数据是线程私有的”,答案包括栈、寄存器、线程局部存储,而堆、全局变量、静态变量是共享的。很多人会把“栈”和“堆”搞反,因为平时写代码时说“new出来的对象在堆上”,感觉堆好像也是线程各自的。其实堆是进程内共享的,只有栈和寄存器是每个线程独立的。如果笔试里遇到“线程A读取了全局变量x,线程B修改了x,是否会出现可见性问题”,还要顺带考察Java内存模型或者C++的atomic/mutex,这就要结合语言特性来答。
这些知识看着散,但复习时完全可以按“高频考点清单”来整理。我会在后面的问题排查部分给出一张速查表,方便考前快速过一遍。
3. 实操实战:手写代码与调试的“隐形得分点”
3.1 审题方法:先列测试用例,再写代码
在笔试编程题中,最冤枉的丢分不是不会做,而是读懂题却做错了。触宝笔试题的题干通常会有两到三段,包含输入输出格式、边界条件和示例。很多同学一看示例就开写,结果遗漏了题干里“如果压缩后的字符串不短于原串则返回原串”这种关键信息。
我的审题方法是:先把题目中的输入输出格式用高亮标注,然后自己在草稿纸上生成三组测试用例。第一组是普通情况,比如 "aaabbc" 期望输出 "a3b2c1";第二组是边界情况,比如空字符串、单字符、全部相同字符;第三组是特殊情况,比如压缩后长度反而变长,如 "abcdefg" 应返回原串。用这三组用例来验证题目要求后,再开始写代码。这样既能防止理解偏差,也能在写完代码后立刻用这三个用例自测。
这个方法看着简单,但能非常有效地区分“会写代码”和“能写出正确代码”的人。笔试的判分系统本身就是用一堆测试用例来跑你的程序,你提前想到的用例越多,一次AC的可能性就越大。
3.2 代码风格与边界处理
别以为笔试判分只看结果,实际上很多公司的笔试系统会有代码人工评审环节,尤其是触宝这种重视技术社区氛围的团队。代码风格和完整度会影响面试官对你的主观印象。
具体来说,注意三点。第一,变量命名要有意义。有人写二分查找,变量名是 i、j、k 三个字母,别人一眼看不懂;如果你写成 left、right、mid,面试官不用注释也能看懂思路。第二,逻辑分支不要嵌套太深,能提前返回就提前返回,这样代码可读性高,也不易出错。第三,边界处理要稳。比如字符串长度是0、数组长度为1、目标值在开头或结尾,这些情况都要有明确的处理分支。
这里分享一个我自己写算法题的模板:先写 if (输入为空) return 结果; 然后初始化左右指针;进入循环时明确循环条件;循环结束后处理剩余情况。只要保持这个稳定节奏,很多低级错误都能避免。触宝的笔试环境一般支持实时编译运行,但不会给你太多测试用例。所以写完代码后一定要自己模拟执行两三遍,特别是在脑子里走一遍循环体的前几次和最后几次迭代,检查指针是否越界。
3.3 时间分配策略:90分钟如何安排
具体的时间分配因题量而异,但以触宝2017第一场的常见配置来看,20道选择+2道编程+1道问答题,90分钟左右。我会这样安排:
前10分钟:快速浏览全卷,标记出需要动笔计算的题和一眼能看出的题。先做有把握的选择题,遇到拿不准的题目暂时跳过,不要在草稿纸上死推。选择题部分整体控制在30分钟内,预留20分钟给第一道编程题,20分钟给第二道编程题,问答题留15分钟,最后5分钟检查试卷是否有空题。
为什么选择题要控制在30分钟?因为选择题虽然分多,但每道平均也就2分钟左右。如果你在一道选择题上花了5分钟还拿不准,基本上说明这个知识点有盲区,再纠结下去也不会突然想通。不如先凭第一感觉选一个,标记下来,如果有时间再回来看。
编程题要先做自己有把握的那道。很多同学习惯从第一题按顺序做,结果第一题是个复杂的状态机,写上半小时还没通过,第二题明明很简单却没时间写了。这是校招笔试里最常见的“顺序陷阱”。我在考场上从来都是先扫描两道编程题的难度,先做思路清晰的那道,确保一道AC,再去攻坚另一道。一道完全正确的大题,往往比两道都只过了一半用例得分高。
问答题虽然不要求写代码,但也不能只写一句话。触宝这类公司的问答题喜欢让你讲一个完整的设计思路,比如“如何设计一个短链接系统”或“手机App端怎么保证接口请求的幂等性”。这种题考察的是结构化的表达:背景-方案-细节-异常处理。你可以在草稿纸上列一个提纲,分点作答。哪怕没有标准答案,但要让面试官看到你有条理。
3.4 在线笔试环境的特殊注意点
触宝2017年那会儿用的在线笔试系统,跟现在的牛客网、赛码网差别不大。但这些系统的代码编辑器和本地IDE差别很大,没有任何自动补全和代码高亮提示,有的甚至连括号匹配都没有。如果平时依赖IDE的自动补全,上到笔试环境会特别不适。
我在考前提醒自己三件事:第一,提前熟悉在线笔试平台的快捷键,比如Ctrl+Enter提交、切换到下一题;不同平台快捷键不一样,不要想当然。第二,代码里不要依赖外部库或自定义包名,笔试系统一般只能支持标准库,有些平台还不支持某些语言的特殊功能,最好提前看平台说明。第三,要注意输入输出的格式,特别是多行输入,有些题目要求读取到EOF,或者逐行读取;如果代码里用 readline 少了循环,就会只处理第一行数据。这个坑我在真实笔试中踩过,当时一道题明明核心逻辑全对,就因为少了一层 while 读取循环,导致只通过了一部分测试用例。
4. 常见问题与排查技巧实录
4.1 典型错误和翻车现场
每次笔试后,我都会让学生把错误复盘出来,发现翻车点高度集中。这里我整理几个最典型的:
一是“数组越界”问题。不管Java还是C/C++,数组越界都可能导致程序直接崩溃或输出错误。比如二分查找里,right=mid-1 后,如果 mid 已经为0,下一次循环可能 left=0, right=-1,这时访问 nums[mid] 就会出错。解决办法是在循环条件里用 while (left <= right),并在访问前判断 left 是否合法。很多人写“while (left < right)”来避免越界,但这样可能会漏掉只有一个元素的情况。
二是“空指针/空引用”问题。例如判断字符串是否为空时,习惯性调用 s.length(),忽略了 s 可能为 null。笔试的测试用例经常故意给 null 或空串。最稳妥的写法是先判断是否为 null,再判断长度。如果语言允许安全调用,也要先确保语义正确。
三是“死循环”问题。多发生在循环条件更新错误的情况下。比如反转链表时,把 p = p.next 写成了 q = p.next,结果指针来回跳,死循环卡死整个程序。这类错误在笔试平台上的反馈是“Time Limit Exceeded”(超时),很多同学以为是自己算法太慢,其实只是死循环。所以写完循环类代码,一定要在脑子里模拟两三轮迭代,看指针是否往前推进。
四是“类型溢出”问题。如果题目数值范围很大,用 int 存结果可能溢出。比如计算数组累加和时,有的同学只看了样例数值小,便直接用 int,但测试数据里可能有一万个数,每个都是10^9,累加就爆。笔试环境里C++的 int 溢出不会报错,Java的 int 溢出也不会报错,就会输出一个奇怪的负数。这种情况下要么用 long/long long,要么在计算过程中取模(如果题目要求)。我在练习时都会养成一个习惯:看题目的数据范围,如果数值上限超过2^31-1,默认用64位。
4.2 智力题/逻辑题如何见招拆招
触宝这类公司偶尔也会在笔试题中放一两道智力题或逻辑推理题,比如“100层楼两个鸡蛋问题”“如何用天平找出次品”“三盏灯对应三个开关”等。这类题并不是真的要你写代码,而是考察你的逻辑思维和归纳能力。
我理解很多同学看到智力题就慌,因为觉得没见过。但智力题的解题套路其实比算法题更固定。首先,遇到问题先尝试从小规模开始递推。以两个鸡蛋100层楼为例,如果你只有两个鸡蛋,又要最坏情况下次数最少,关键不是一层层扔,而是构建一个步长递减的搜索过程。第一次从第14层扔,如果碎了就逐层试1到13层;没碎就第二次从27层扔,再碎就在15到26层逐层试,以此类推。14+13+12+...+1 = 105 > 100,所以14是第一步的最优值。这就是“找临界点”的思路。
这种题在笔试中占比不高,如果实在没有头绪,可以先跳过,不要因此影响后面的编程题。等编程题做完,再回来用“极端假设法”和“举例归纳法”慢慢推。如果你平时能花点时间刷一下经典智力题的题解,考场上的反应速度会快很多。
4.3 独家避坑:高频考点速查表
根据触宝2017秋季校招研发笔试题的考点方向,结合历年相同风格公司的笔试内容,我总结了一张高频考点速查表,考前可以用它快速自查。注意,这不是全部范围,但覆盖了大部分研发笔试可能出现的重点。
| 知识模块 | 必背核心点 | 常见坑与提示 |
|---|---|---|
| 数据结构 | 数组和链表区别;栈和队列应用;Hash冲突解决;二叉树遍历 | 递归转迭代时容易忘栈存储;哈希扩容的复杂度 |
| 算法 | 二分查找变体;快排和归并排序;DFS/BFS;动态规划入门 | 注意重复元素和边界溢出;DP要会定义状态与转移方程 |
| 操作系统 | 进程和线程区别;死锁四条件;虚拟内存;并发同步机制 | 线程私有资源是栈和寄存器;线程安全要会写加锁示例 |
| 计算机网络 | TCP三次握手;四次挥手;HTTP状态码;TCP和UDP区别 | 第二次握手SYN和ACK都为1;GET和POST区别要结合实际场景 |
| 数据库 | SQL连接查询;索引原理;事务ACID;范式 | 最左前缀原则;索引失效的情况;不要只看结果不分析执行计划 |
| 语言基础 | 变量的作用域;参数传递方式;垃圾回收机制;异常处理 | Java传值还是传引用要看对象的引用;Python默认参数是可变对象时要小心 |
| 逻辑题 | 递推、反证法、极端假设法;染色问题;天平分组 | 先假设最简单情况,找规律;不要上来列方程 |
这张表不需要逐条死记,而是在笔试前两小时快速过一遍,重点看“常见坑”这一列,唤醒你对易错点的记忆。比如看到TCP第二次握手,立刻提醒自己SYN和ACK都要置1;看到线程私有资源,立刻提醒自己栈和寄存器。这种条件反射式的记忆,在选择题里尤其好用。
4.4 复盘方法:从笔试到面试转化的关键
笔试结束后,不管发挥如何,一定要趁热打铁做一次深度复盘。很多同学笔试完就丢到一边,下次笔试继续犯同样的错,这是最亏的。
复盘时不要只看分数,要把每道题重新做一遍。选择题要理解每个选项为什么对、为什么错,而不只是记住正确答案。编程题如果当场没AC,下来后在本地IDE里慢慢调试,搞清楚是思路问题还是边界问题。问答题可以找同学或者网上搜类似题目,对比自己的答案看漏了哪些要点。
把复盘笔记按知识点分类整理,比如“链表”“二分”“并发”“网络”等。等积累了三四场笔试的错题,你就能看出自己的薄弱模块,然后针对性地刷题。我在带学弟学妹时经常说:“笔试不是一次性的考核,而是一个查漏补缺的工具。”如果能在秋招前把常见的坑都踩一遍,之后的笔试会越来越顺。
写在最后的小建议
看了触宝科技2017秋季校招研发笔试题(第一批)的题目风格,我个人最大的体会是:这家公司考的不是智商,而是你作为一名研发人员的基本素养——能不能在压力下保持代码的严谨性,能不能把学过的基础知识灵活运用到实际问题里。这些能力不是靠考前突击模板就能练出来的,需要平时写代码时多思考一步“如果数据是空的怎么办”“如果并发访问怎么办”。如果你能把每个小问题都从原理层面想透,那不管笔试题怎么包装,你都能识别出它的内核。
最后再分享一个小技巧:做笔试时,遇到不会的知识点,可以先把题目里涉及的场景关键词记下来,等笔试结束后,用这些关键词去搜索,往往能找到一线工程师写的踩坑总结,比死记硬背课本效率高得多。希望这篇拆解对你有所帮助,也祝你后面每一场笔试都能稳定发挥,拿到心仪的面试机会。