公司动态
2024春招小红书研发岗笔试复盘:题型解析与避坑指南
2024年春招的小红书研发岗第二批笔试已经过去一阵子了但后台还是陆续有人私信我问当时考了什么、难度怎么样、后续有没有面试消息。趁这几天把记忆里的内容重新捋了一遍把这次笔试的题型分布、算法题思路、时间分配策略和一些容易踩的坑整理成一篇完整的复盘。不管你是准备投下一批秋招还是纯粹想看看互联网大厂笔试现在是个什么难度这篇应该都能给你一些参考。先说结论这次笔试的整体难度中等偏上题型分两大块——客观选择题和两道编程题。选择题覆盖面比较广操作系统、计算机网络、数据库、Java/Golang基础都有涉及编程题一道偏数据结构实现一道偏动态规划整体不是那种纯刷题就能碾压的难度更看重思路是否清晰、代码基本功扎不扎实。接下来一个一个拆开讲。1. 笔试整体感知与准备方向1.1 批次安排和笔试平台的大致情况小红书每年的校招笔试都会分多个批次滚动安排2024年春招的研发岗也延续了这种节奏。第二批笔试和第一批的时间间隔不算太长所以从第一批考完到第二批之间坊间已经流传了一些题型回忆大致能看出出题风格——选择题比重不小而且不是那种送分题很多都带实际场景。第二批的题目整体框架和第一批是类似的这算是比较幸运的地方等于变相给了后来者一个不大不小的信息差。笔试用的平台是市面上比较常见的在线笔试系统支持多种主流语言。有一点要特别提醒这类平台普遍都是ACM模式也就是你需要自己处理输入输出格式。很多平时习惯在LeetCode上写核心代码的同学第一次接触这种模式会非常不适应我见过不少人挂在IO处理上题目本身会做但输入解析写错了最后0分。这个细节后面我会单独讲。1.2 第二批笔试的题型构成从整体结构来看这次笔试大致分成两部分第一部分是选择题大约20道左右涉及计算机基础四大件加一部分语言特性。第二部分是编程题一共两道难度递进第一道偏简单/中等第二道偏中等/困难。选择题的数量看起来不算特别多但每道题都需要一定的思考时间尤其是那些结合场景的分析题比单纯背概念要费时间得多。编程题两道题的分值占比通常比选择题高不少是拉分的主要战场所以时间分配上要格外注意。从第二批的实际反馈来看选择题里Linux命令、进程线程、数据库隔离级别这些几乎是必考项编程题则比较偏爱考数组操作、动态规划、贪心思想偶尔会掺一点树和图的题。下面我把各部分逐一拆开给出具体的考点和解题思路。2. 选择题高频考点拆解这一部分我按科目整理了一下方便大家对照排查自己的薄弱点。选择题的分布不一定是均匀的但下面这些方向确实出现了不止一次后续批次的准备可以重点覆盖。2.1 操作系统与Linux命令操作系统的题主要集中在进程管理、线程模型、死锁和内存管理这几个模块。比如进程和线程的本质区别、上下文切换开销、死锁的四个必要条件、进程间通信方式管道、消息队列、共享内存、信号量各自的特点和适用场景这些都属于高频考点。这次给我印象很深的一道题是关于死锁的给出一段多线程并发场景问哪种方式不能有效避免死锁。选项里有互斥锁、银行家算法、资源有序分配法、信号量。表面上看四个选项都和“并发控制”有关但仔细分析就会发现互斥锁本身是产生死锁的条件之一它不能“避免”死锁只是提供互斥能力。这种题就是典型的“看起来都会一做就错”考的就是对概念的深度理解而不是死记硬背。Linux命令也是选择题里出镜率很高的一块。常考的无非是文件权限管理chmod、chown、进程查看ps、top、kill、网络工具netstat、ping、traceroute、文本处理grep、awk、sed。其中awk和sed的组合使用是很多人的盲区建议把这两个命令的常见用法刷熟练尤其是awk的字段分割和内置变量基本每年都会出题。2.2 计算机网络与数据库计算机网络的重点非常固定TCP三次握手和四次挥手、TCP与UDP的区别、HTTP状态码语义、HTTPS的握手流程和加密机制、DNS解析过程、Cookie与Session的区别。这次考了一道TCP拥塞控制的题问的是慢启动阶段窗口大小如何增长以及什么情况下会进入拥塞避免。这类题其实不难但如果对《计算机网络》里的那张拥塞窗口变化图没有形成直观记忆现场推导很容易乱。数据库这块SQL语法是一个必考点但它考的不是简单的select而是多表join、子查询、group by having的组合。所以不要只背单表查询连表查询的各种写法一定要亲手在本地跑几遍。另一个高频考点是事务隔离级别读未提交、读已提交、可重复读、串行化这四种隔离级别分别解决什么问题、会产生什么并发异常必须能默写出来。MVCC的底层实现undo log版本链、ReadView规则也是近几年互联网公司笔试的常客值得深入看一遍。2.3 数据结构与语言基础数据结构的选择题不算特别多但会考一些需要计算的复杂度问题。比如给定一个递归式让你求时间复杂度——主定理是这种题最快的解法建议专门看一下。另外哈希表冲突解决方法、二叉树遍历序列的互推已知前序中序求后序这类题也出现过。语言基础部分Java和Golang都会考一点。Java这边HashMap在JDK 7和JDK 8之间的区别红黑树引入、头插法变尾插法、ConcurrentHashMap的锁粒度变化、JVM内存区域划分和GC Roots这些都算高频中的高频。Golang这边goroutine与channel的基本使用、GMP调度模型、slice和array的区别考察的概率也不低。如果你主语言是Java建议至少把Goroutine和channel的基础概念过一遍不然遇到Golang的选择题会比较吃亏。下面用一张表总结一下选择题的高频考点和复习优先级科目高频考点复习优先级操作系统进程线程、死锁、内存管理、Linux命令高计算机网络TCP/IP、HTTP、HTTPS、DNS高数据库SQL、隔离级别、MVCC、索引原理高Java基础HashMap、JVM、并发编程高Golang基础goroutine、channel、GMP模型中数据结构复杂度分析、二叉树、哈希表中3. 编程题全复盘与解题思路编程题是笔试的核心也是区分度最高的部分。第二批次的两道题我按回忆整理出来题型和原题不完全一致但考察的知识点和难度水平是很接近的。我会给出详细的思路推导和可跑的代码方便你照着练。3.1 第一题连续子数组区间计数这道题的大意是这样的给定一个长度为n的整数数组和一个目标值k要求统计数组中所有满足“子数组元素之和小于等于k”的连续子数组个数。n的范围大概是10的5次方所以O(n^2)的暴力解法必挂需要优化到O(n)。第一眼看到这个题思路其实很清晰连续子数组求和自然想到前缀和。定义前缀和数组pre[i]表示前i个元素的和那么子数组[j, i]的和就是pre[i] - pre[j-1]。问题转化为对于每个右端点i寻找有多少个左端点j使得pre[i] - pre[j-1] k也就是pre[j-1] pre[i] - k。因为数组中没有负数所以前缀和天然具有单调性。既然单调就可以用双指针维护一个滑动窗口右指针每向右移动一位左指针跟着移动直到窗口内的和不超过k然后以右指针结尾的合法子数组数量就是窗口长度。这个题其实还可以玩出另一个变体如果数组里有负数前缀和就不单调了双指针就不成立得用前缀和离散化树状数组的方式去求逆序对数量复杂度会上升到O(n log n)。如果笔试里遇到“数组可能包含负数”的条件一定要警惕不要无脑滑动窗口。回到这个题双指针解法的代码非常简单def count_subarrays(nums, k): n len(nums) left 0 current_sum 0 ans 0 for right in range(n): current_sum nums[right] while current_sum k: current_sum - nums[left] left 1 ans right - left 1 return ans这里面有一个很关键的点为什么右指针每次移动后答案要加上right - left 1因为固定右端点以left到right之间的任意位置作为左端点形成的子数组都满足条件数量正好等于当前窗口长度。这个思路一定要想明白很多类似的滑动窗口计数题都是同一个套路。3.2 第二题带冷却时间的任务调度第二题明显比第一题高一个档次是一道经典的带冷却时间任务调度题。题目大致是这样的给定一个字符数组tasks每个字符代表一种任务类型每个任务需要1个单位时间执行两个相同任务之间必须间隔至少n个单位时间冷却时间求完成所有任务所需的最短时间。这题在LeetCode上有个几乎一样的题叫“任务调度器”经典解法是用贪心统计每种任务的数量找到出现次数最多的任务把它作为骨架来排布。举个例子如果任务A出现5次冷却时间n2那A的排布就是A _ _ A _ _ A _ _ A _ _ A基本上可以想象成先放5个A每个A后面跟两个空位最后再补一个A。总时间骨架就是(max_count - 1) * (n 1) max_num其中max_count是最大出现次数max_num是达到最大次数的任务种类数。但这个公式计算出来有时会小于tasks数组本身长度。比如任务的种类特别多填充物足够填满所有空隙那么实际最短时间就是tasks的总长度。所以最终答案是两者取较大值def least_interval(tasks, n): from collections import Counter counts Counter(tasks) max_count max(counts.values()) max_num sum(1 for v in counts.values() if v max_count) return max(len(tasks), (max_count - 1) * (n 1) max_num)这里要注意max_num表示出现次数等于最大值的任务有几个。当有多个任务都达到最大次数时收尾部分需要多留出对应数量的位置。这个细节很容易漏漏掉的话算出来的结果会偏小。这道题考察的核心其实是贪心思维和数学推导能力。很多人第一反应是模拟每个时间片安排什么任务但模拟的复杂度比较高且容易出错。而上面的公式其实是把问题抽象成了“插空”模型出现频率最高的任务是瓶颈其它任务只要能填进空档就不会增加总时间如果空档不够填说明任务总时长本身就更大那就直接返回总长度。从这道题延伸开笔试里很多调度类题目都可以用类似的“找瓶颈 插空验证”的思维来解决。拿到题目时先别急着动手编码先把数学模型建立起来往往能把一道看上去复杂的题瞬间简化。3.3 两道编程题的对比与做题顺序建议两道题放在一起对比能明显看出出题人的意图第一题考的是数据结构和基础算法前缀和/滑动窗口第二题考的是贪心思维和数学建模能力。第一题更偏向“基本功”第二题更偏向“思维深度”。我个人的做题策略是先把两道题都快速扫一遍然后从第一题开始做。因为第一题通常更容易拿分做完后的心态会稳很多再去啃第二题就不会慌。第二题如果实在没思路也不要完全不写把统计频次的代码写出来至少能过一部分测试用例拿一部分分数。还有一个很多人会忽略的点如果你的思路是“模拟时间片一个个推进”但实现到一半发现逻辑越来越复杂、边界情况越来越多大概率说明这条路走不通。这时候别死磕果断退出来重新想贪心或者数学解法。在线笔试的时间是很宝贵的死磕一道题导致第二道题没时间做是最亏的结果。4. 时间分配与做题策略4.1 时间分配的整体思路小红书这批笔试的总时长大概在90分钟到120分钟之间选择题数量加上两道编程题时间不算充裕。我的建议是把时间大体切成三块选择题控制在40分钟以内编程题第一题控制在20-25分钟第二题控制在30-35分钟剩下10分钟左右用来检查。选择题一定要控制住时间。有些选择题会故意设计得很有迷惑性如果你在某一题上卡了5分钟以上大概率是做不出来的先标记一下跳过去把后面的题先做完再回来看。笔试系统一般允许跨题跳转所以要充分利用这个功能不要在一道题上恋战。编程题的读题也非常关键。我遇到过不少同学题目没读完就急着写代码结果写到一半发现理解错了又推倒重来。两道编程题读题至少花3-5分钟把输入输出的格式、边界条件、时间空间限制全部看清楚再开始动手。尤其是数据范围这直接决定了你的算法必须达到什么复杂度级别。4.2 做题顺序的战术选择做题顺序其实是个很讲究的事情。一般来说先做编程题、后做选择题还是先做选择题、后做编程题要因人而异。我的习惯是先快速浏览所有题目然后先做编程题里自己有把握的那道再做选择题最后回来死磕剩下的编程题。为什么这么排因为编程题耗费的精力和时间比较多放在前面做大脑状态好思路清晰更容易AC。选择题毕竟有选项有时候即使知识点不太熟也能靠推理和排除法蒙个大概。如果把编程题放在最后经过大量选择题轰炸之后大脑已经很疲劳了再做编程题容易反应变慢思路打不开。当然这个策略不是绝对的。如果你明显感觉选择题里有很多送分题先迅速拿掉这些分也很重要。关键是心里要有一杆秤选择题的分是“易得分”编程题的分是“高分值”两边都要稳住不能顾此失彼。4.3 遇到不会的题怎么处理笔试中遇到不会的题太正常了。编程题卡死的时候我常用的办法是先跳出来在草稿纸上画例子。很多时候思路是在拿小样例一步一步手推的过程中找到的。比如那个任务调度题如果一时想不起来公式拿一组tasks和n手动排出最优解排着排着就能总结出规律来。选择题遇到完全没头绪的先排除明显错误的选项然后凭第一感觉选一个做个标记。有时间再回来看没时间就保持原样。千万不要空着不选很多笔试系统空着是0分选错也不倒扣分所以蒙一个总比空着强。还有一点要提醒有些编程题是支持部分通过的也就是说你的代码只要能在部分测试用例上跑通就能拿到一部分分数。所以即使你的答案不是最优解只要符合基本逻辑就把代码写上去。完全空着或者只输出一个固定值是最不明智的。5. 常见问题与避坑清单这部分我把自己见过的、身边同学踩过的坑集中整理一下后续参加笔试的同学可以对照着自查。5.1 在线IDE与本地环境的差异笔试平台的在线IDE通常没有本地IDE那么完整的调试功能不能打断点、不能逐步调试。很多人写着写着变量值不对只能靠print大法去排查非常痛苦。所以平时练习的时候就要适应在“没有debugger”的情况下写代码。我自己的做法是把关键变量的变化过程用注释标注在旁边逻辑推演清晰后再落笔减少试错成本。另外在线IDE的代码补全功能通常比较弱语法高亮可能也不太稳定。如果你平时重度依赖IDE的自动补全和错误提示在笔试环境下会非常难受。提前在牛客或者力扣上用ACM模式做几套题把输入输出处理的肌肉记忆练出来是个很好的预演。5.2 输入输出处理的坑这是ACM模式最容易翻车的地方没有之一。比如题目给了一行整数用空格分隔你需要读进来存成数组。有些人用input().split()之后忘记转int直接拿字符串去运算结果全错。再比如多行输入有些人写了一个循环去读结果读多了或者读少了导致数组越界。提供几个我常用的输入输出模板import sys # 读取一行整数 data list(map(int, sys.stdin.readline().strip().split())) # 读取n行每行多个整数 n int(sys.stdin.readline().strip()) arr [] for _ in range(n): arr.append(list(map(int, sys.stdin.readline().strip().split())))这种模板本质上就是固定写法多练几遍就能形成肌肉记忆。考试的时候把这部分代码默写出来不会占用太多时间但能避免大量低级错误。输出方面注意题目要求的是空格分隔还是换行分隔很多人在这个细节上失分非常可惜。5.3 边界条件与极端用例边界条件是编程题最常见的失分点。数组为空、只有一个元素、所有元素相等、数值达到最大值这些情况你是否都考虑到了写代码的时候先花30秒想清楚边界条件比测试的时候到处找bug要高效得多。还有个常见的坑是整数溢出。如果题目给的n是10^5级别子数组的和可能超过int的范围这时候就要用long longC或者Python天然支持大整数所以无所谓但Java、Go之类的语言就要小心类型范围。类似的如果题目要求结果对某个数取模记得每一步都取模不要等到最后再取防止中间结果溢出。5.4 心态管理与时间预警笔试的心态太重要了。我见过一个同学第一道编程题卡了很久没写出来然后整个人就慌了后面的选择题也做得很差最后成绩惨不忍睹。这种连锁反应其实是可以避免的——做题前就给自己定好规矩一道题如果15分钟没有任何进展立刻放弃做下一道全部做完以后有时间再回来啃。在线笔试系统一般会有倒计时提醒但我建议你不要依赖它。每做完一道题扫一眼时间心里大致有个数。做题过程中不要频繁看倒计时那样只会增加焦虑感。最后再分享一个小技巧笔试前把环境准备好包括稳定的网络、安静的场地、充满电的设备、水放在手边。听起来都是小事但任何一项出问题都会打断思路。准备工作做得越充分笔试的时候就越能聚焦在题目本身。考完以后无论感觉是好是坏迅速把题目和思路记录下来这不是为了对答案而是为下一批笔试积累素材。每一场笔试都是下一场的演练我就是靠着这么一轮一轮攒下来的经验才在后来的面试和笔试里越来越从容的。