2023年秋招季,我在赛码网上连着做完了携程研发岗第一批笔试的整套题,交卷那一刻脑子里只有一句话:这套题的区分度,比想象中高不少。这两年“2023携程秋招研发岗笔试”在牛客、知乎、赛码讨论区的热度一直不低,核心原因有三个:一是携程本身的技术盘子以Java为主,考察内容和主流大厂高度重合;二是题目设置里既有纯算法题,也有贴近业务场景的工程题,比很多“只刷LeetCode就能过”的厂要讲究;三是赛码网这个平台对输入输出的要求和大厂机考规范很接近,提前练透能少踩很多坑。
这篇文章想做的事很明确:把2023年携程秋招研发岗第一批笔试涉及的核心模块、题型特征、做题思路和现场操作要点完完整整拆一遍。不管是参加携程后续批次的应届生,还是准备其他大厂研发岗笔试的人,都能从里面拿走一套可执行的方法。我不会贴原题去凑数,因为每家公司的题库都在动态更新,但题型结构、考察逻辑和准备方向是有规律可循的,这些才是真正值钱的东西。
1. 先画好携程笔试地图:流程、题型、批次差异
1.1 考试流程与平台:赛码网、时长、题量
先明确一件事:笔试不等于面试,笔试的核心目标是“用最短的时间筛掉基础不过关的人”。2023年秋招携程研发岗笔试在赛码网进行,考试总时长大约120分钟,题型一般包含单选题、多选题和2到4道编程题。不同批次、不同岗位方向(Java、C++、前端、算法)在具体配比上会有差异,但整体框架类似。第一次在赛码网上打比赛的话,最需要注意的是它的代码提交方式——不是白板手写,也不是本地IDE自动补全,而是网页编辑器加标准输入输出。这意味着你平时在本地IDE里能跑通的代码,粘到赛码网上可能因为Scanner没读完整、输出多了一个空格、Long类型越界等问题直接判错。
很多同学在准备阶段习惯用LeetCode,但LeetCode的代码模板是“填空式”的,函数签名、参数类型、返回值都给你定好了。赛码网这种OJ平台则是从空白Main类开始,所有输入都要自己读,输出要自己拼,这对习惯了LeetCode模式的人是个不小的冲击。我见过好几个算法水平不差的人,第一次在这种平台上做笔试,光是处理输入输出就花了十几分钟,后面编程题时间不够用。所以准备携程笔试,不能只在LeetCode上刷,一定要去赛码网或其他OJ平台做几套完整模拟,把“读入-计算-输出”这套流程练成肌肉记忆。
1.2 题型配比里的信号
从第一批的题型结构来看,编程题占比最高,是拉开差距的主战场;选择题侧重基础,Java、数据库、操作系统、网络四块是常客。这个配比反映了一个判断:携程愿意让你在算法题上展示思路,同时通过选择题快速检查你的基础广度。换句话说,只刷题不背理论的,选择题容易翻车;只背八股不写代码的,编程题直接暴露。两类人都拿不到好结果。
具体来说,选择部分大概在15到25道之间,编程题通常有2到4道,难度梯度从“签到题”到“压轴题”。签到题一般是字符串处理或简单模拟,给全卷送一档分;压轴题往往是动态规划或图论,用来卡高分段。这里有个很实用的判断标准:如果你能在一个半小时内把除压轴题之外的题目都稳定做对,笔试通过的概率已经很高了。很多人总盯着最后一题死磕,反而忽略了前面那些“白送分”的基础题,这是完全错误的策略。
1.3 第一批的特殊价值
“第一批”的价值在于它是整个秋招风向标的提前释放。第一批题目一旦出来,后续批次的题型大致方向就不会有太大偏移,无非是难度调高或调低、个别考点换皮重组。所以如果你投的是后面几批,把第一批的题型结构、难度梯度吃透,相当于提前拿到了考试大纲。这个意义比“背住某道题”大得多。
举个例子,第一批卷子里出现了“行程时间衔接”类的图论题,那么后续批次大概率也会围绕旅游业务场景出图论或动态规划题,只是具体数据规模和包装方式会变。我把这个规律称为“题型惯性”:大厂笔试出题组会在一个时间段内使用同一套题库模板,反复微调。利用这个特点,投递时尽量选前几批,第一批上岸机会最大;如果只能参加后面的批次,也一定要把前面批次的面经和题型讨论翻个底朝天。
2. 编程题:4类高频考点和一套稳定拿分打法
2.1 高频考点频率排序
从2023年第一批及同期的行业反馈来看,出现频率最高的算法考点集中在:动态规划、字符串处理、图论(尤其最短路和拓扑排序)、模拟题、双指针和滑动窗口。携程的业务是旅游出行,场景化包装的题目很常见,比如“给定多个行程段,找出满足时间衔接的最优路径”,本质是图的最短路径;“一组酒店价格序列,计算最大收益”,本质是股票买卖类DP。题目会套上业务外壳,但内核还是经典算法。
我给备考的人一个框架:把笔试编程题分成“模型识别”和“模板默写”两步。模型识别就是快速判断这道题属于DP、图论、字符串还是模拟;模板默写就是把你背熟的标准解法写到代码里。这两步缺一不可。很多人上考场才现场推算法,效率极低;真正高分的人,看到题干就能基本锁定解法框架,只剩边界条件需要现场处理。这种“应试反射”靠的就是平时大量归类训练。
2.2 动态规划:性价比最高的拿分点
DP是我建议优先准备的重点,因为它在互联网大厂笔试里的出现概率极高,而且题型套路相对固定。携程第一批里有道典型的序列型DP,题目大意是“给定一个长度为N的整数数组,每个位置表示当天可获得的积分,不能连续两天选择,求最大累计积分”。这个题就是打家劫舍的变体,状态转移方程是 dp[i] = max(dp[i-1], dp[i-2] + nums[i]),初始值 dp[0]=nums[0],dp[1]=max(nums[0], nums[1])。看起来简单,但现场有不少人栽在边界条件上,比如 N=1 时越界、数组全负数时结果不对。
我自己的习惯是:见到“约束条件下求最大值/最小值/方案数”这类字眼,先往DP方向想;写出转移方程后,先对三个边界样本(最小规模、全相同值、全逆序)做脑内推演,再动手写代码。这个流程能筛掉至少一半低级错误。用代码写就是下面这样,注意处理N=1的边界:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); long[] nums = new long[n]; for (int i = 0; i < n; i++) { nums[i] = sc.nextLong(); } if (n == 1) { System.out.println(nums[0]); return; } long[] dp = new long[n]; dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); for (int i = 2; i < n; i++) { dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); } System.out.println(dp[n - 1]); } }这里我特意用 long 而不是 int,因为积分数组求和到后期很可能超过 int 上限。笔试里的数据范围经常不声不响设到10^9级别,几个数一加就爆了。用 long 做中间变量不会有什么副作用,却可以避免一大类WRONG ANSWER。类似的DP题还有背包、最长递增子序列、编辑距离,这些都是按套路走的题型,值得优先刷熟。
2.3 字符串与模拟:稳拿分项不能丢
字符串题经常被低估。携程这种业务系统里,字符串处理无处不在,所以笔试里出现“解析时间字符串并比较先后”“日志格式去重统计”这类题一点也不奇怪。这类题考察的不是算法难度,而是编码的严谨性:split的时候空字符串会不会进来、时间换算要不要考虑跨天、哈希统计用什么数据结构。
我举个简单的例子:输入若干行“日期 + 访问量”,输出每天的总访问量,并按日期排序。这个题核心就是String.split + HashMap + TreeMap排序,代码量不大,但容易栽的地方是日期格式不统一、输入行可能有空行、统计结果要按时间字符串排序而不是按输入顺序。这些细节只要有一个没想到,测试用例就过不全。
对模拟题的办法只有一个:别慌,把规则翻译成代码,用纸上演算的方式跑一遍样例。我见过太多人因为模拟题题干长就直接放弃,其实这类题恰恰是最容易拿全分的。题干越长,信息越多,反而越不需要你“临场发明算法”,只要耐心拆解流程。
2.4 图论问题:最短路径和拓扑排序是热门
图论题在研发岗笔试里属于中高难度,但考察点很集中。一个是单源最短路,Dijkstra的堆优化写法必须背熟;另一个是拓扑排序,经常和“任务依赖”“课程安排”这类题干绑定。携程的业务里有大量库存、订单、运力的依赖关系,所以拓扑排序类题目的出现有天然的业务合理性。
建议把以下模板练到“闭眼能写”:邻接表建图、优先队列做Dijkstra、入度数组做Kahn拓扑排序。光看是不够的,一定得在编辑器里亲手敲三遍以上,因为笔试时真的会紧张到手抖。手抖的时候,人的记忆会退回最熟练的模式。如果这个模式还没固化,那基本等于现场从零开始写。
给一个Dijkstra的Java模板参考,重点在优先队列的比较器写法:
import java.util.*; public class Main { static class Node { int idx; long dist; Node(int idx, long dist) { this.idx = idx; this.dist = dist; } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int m = sc.nextInt(); List<List<int[]>> graph = new ArrayList<>(); for (int i = 0; i <= n; i++) { graph.add(new ArrayList<>()); } for (int i = 0; i < m; i++) { int u = sc.nextInt(); int v = sc.nextInt(); int w = sc.nextInt(); graph.get(u).add(new int[]{v, w}); } long[] dist = new long[n + 1]; Arrays.fill(dist, Long.MAX_VALUE); dist[1] = 0; PriorityQueue<Node> pq = new PriorityQueue<>((a, b) -> Long.compare(a.dist, b.dist)); pq.offer(new Node(1, 0)); while (!pq.isEmpty()) { Node cur = pq.poll(); if (cur.dist > dist[cur.idx]) { continue; } for (int[] edge : graph.get(cur.idx)) { int next = edge[0]; long nextDist = cur.dist + edge[1]; if (nextDist < dist[next]) { dist[next] = nextDist; pq.offer(new Node(next, nextDist)); } } } System.out.println(dist[n] == Long.MAX_VALUE ? -1 : dist[n]); } }这段代码的细节在于“cur.dist > dist[cur.idx]”这个懒删除判断。没有它,同一个节点可能会被多次从堆里弹出,导致性能退化甚至超时。这就是模板默写要到达的精细度。
2.5 线上环境与输入输出细节
赛码网是标准输入输出模型,第一题如果给了多组测试样例,必须用 while(scanner.hasNext()) 或类似循环来处理,不能用“只处理一次”的写法。数组输入如果第一行是长度,第二行是元素,建议先用 nextInt() 读长度,再循环读元素,避免 nextLine() 和 nextInt() 混用导致的换行符问题。输出格式上,要求行尾不能有空格时,可以用一个标志位来控制,或者先把结果收集到 List 再 join 输出。
这些细节别看小,系统判定是全文匹配,一个多余空格就是一次 Wrong Answer。还有就是类名,赛码网一般要求主类叫 Main,方法签名要固定,如果类名写成了自己的类名,直接编译错误。2023年第一批笔试结束后,有同学在讨论区反馈“代码本地能跑但平台编译失败”,绝大多数是这两个原因:类名不对,或者引用了没导入的包。
3. 基础选择题:Java/C++、数据库、网络复习清单
3.1 Java基础:集合、并发、JVM是三大台柱
携程后端以Java为主,所以Java基础选择题在笔试里占比不小。第一个高频区是集合框架,HashMap的底层结构、扩容机制、ConcurrentHashMap在JDK 1.8前后的变化,基本年年出现。第二个是并发,volatile的可见性与禁止重排序、synchronized的锁升级、线程池的核心参数含义,都是常客。第三个是JVM,内存区域划分、垃圾回收算法、类加载机制,尤其“双亲委派”这个概念,做选择题时要注意“是否会被打破”“常见打破例子”这些变体。
复习时不要只背结论,要能解释“为什么这样设计”。比如ConcurrentHashMap为什么在1.8放弃分段锁,改成CAS加synchronized?因为分段锁的锁粒度是段,一段里还有多个槽位,竞争依然存在;CAS加链表头节点锁能让锁粒度缩小到单个桶。理解了这一点,碰到“JDK1.8的ConcurrentHashMap在什么情况下会加锁”这种变体题,你就能推出答案是“链表头节点或红黑树根节点”,而不是靠背选项。
这里给一道很容易出错的判断题:“HashMap允许key为null,ConcurrentHashMap也允许key为null。”答案是错的。原因是ConcurrentHashMap在并发环境下无法区分“key不存在”和“value为null”,所以干脆禁止null。这种“看似相同实则不同”的对比,是选择题最喜欢挖的坑。
3.2 C++方向的同学:内存管理和STL是底线
如果你是C++岗,复习重心放在内存管理、STL底层、指针与引用区别这三块。C++的笔试题很喜欢考“下面哪个代码会内存泄漏”“vector扩容时迭代器是否失效”这类细颗粒度问题。这里给一个提醒:笔试和面试不一样,选择题往往把“看起来都对”的选项放在一起,靠的就是你对底层机制的记忆精度。vector的扩容倍数在标准里不是固定的,常见实现是2倍或1.5倍,题目如果直接问“一定是2倍”,那就是错的。
内存泄漏题通常会给一个局部指针new出来的对象,函数退出后没有delete。但C++11之后,智能指针的引入让这类题有了新变体:shared_ptr循环引用导致内存无法释放。如果你只在八股文里背过“shared_ptr会引用计数”,却不知道怎么处理循环引用,题目稍微变形就会卡住。解决办法很简单,记住weak_ptr是用来打破循环引用的。
3.3 数据库与SQL:性价比被严重低估
数据库题是很多人的软肋,但恰恰是笔试里性价比很高的模块。2023年第一批里出现了索引失效场景判断和SQL编写题,难度不算高,现场却有不少人没写出来。复习时抓住三个核心:索引结构(B+树)、索引失效的常见场景(最左前缀原则、隐式类型转换、函数包裹索引列)、以及慢查询分析的基本流程。
这里列一个索引失效速查表,笔试前过一遍就能避免大部分误判:
| 场景 | 是否失效 | 原因 |
|---|---|---|
| 联合索引(a,b)只查b | 失效 | 违背最左前缀 |
| where a = 1 and b = 2 | 生效 | 符合最左前缀 |
| where 函数(a) = 1 | 失效 | 索引列被函数包裹 |
| where a = 100(a是varchar,传int) | 失效 | 隐式类型转换 |
| like '%abc' | 失效 | 前缀模糊无法走索引 |
| like 'abc%' | 可能生效 | 前缀匹配可利用索引 |
SQL编写题建议把所有筛选条件先用WHERE表达清楚,再做GROUP BY分组,最后用HAVING做组级过滤,这个顺序是标准逻辑,写复杂SQL时不容易乱。举个例子,查“每个城市预订量超过100的酒店数量”,先按城市和酒店分组统计预订量,再用HAVING过滤超过100的酒店,最后对城市做COUNT聚合。很多人一上来就GROUP BY,把WHERE条件写进HAVING里,逻辑就歪了。
3.4 操作系统和网络:抓主线考点
操作系统重点看进程与线程的区别、死锁的四个必要条件、虚拟内存和页面置换算法;网络重点看TCP三次握手四次挥手、TCP和UDP区别、HTTP状态码语义、HTTPS的握手过程。这些考点在选择题里几乎是必出的。复习技巧是画时间线图辅助记忆,比如TCP的状态迁移,自己对着一张图把每个转移条件讲出来,讲不出来就是没记住。
网络题有个高频陷阱:HTTP状态码的语义。201表示什么?很多人记住是Created,但在RESTful接口里有特殊情况。302和307的区别、304和缓存的关系,这些都是选择题喜欢咬文嚼字的地方。我的建议是把常见状态码按“2xx成功、3xx重定向、4xx客户端错误、5xx服务端错误”分层记忆,每层挑三四个代表,语义记精确,不要模棱两可。
4. 实操复盘:投递后48小时和考场上的取舍
4.1 投递之后到笔试前的48小时怎么用
我在秋招时有个习惯:投递简历后立刻去搜这家公司往年的笔试题型和面经,把信息整理成一页纸,然后按两天时间安排复习。第一天主攻算法编程题,把DP、字符串、图论三类高频模板各默写两遍;第二天刷基础题,Java集合/并发、SQL、网络各过一遍,并把容易混淆的知识点做成一个错题本。
这种“针对性补强”比漫无目的刷100道LeetCode有效得多,因为笔试准备不是要你成为算法大师,而是要在有限时间内把最可能出现的考点练到肌肉记忆。以携程为例,如果已经知道考Java基础,那就没必要花大量时间啃C++的虚函数表;如果知道有图论题,就把邻接表和Dijkstra模板写两遍,比新学一个冷门算法更实用。
时间安排上,我倾向于把编程题模板默写放在上午,因为大厂笔试通常安排在下午或晚上,上午练习能保持手感和精神状态。下午用来做一套完整的模拟卷,严格计时,体验真实的做题节奏。晚上复盘错题,把选择题里模糊的知识点在面经帖子里找到对应答案,记进错题本。这一套下来,48小时足够让状态上一个台阶。
4.2 实战答题顺序:先拿稳分再攻坚
我的建议是:拿到卷子先花2-3分钟浏览全部题目,把编程题按难度标号,先做自己最有把握的题,再做中等难度的题,最后啃最难的题。选择题按顺序做,但单题超过90秒就标记跳过,不要恋战。编程题里如果一道题卡了超过20分钟,果断换下一道,把能拿的分先拿到手。笔试是总分为王,不是单题英雄主义,一道题全对不如四道题各拿一半。
这里有个细节:编程题即使没完全做对,只要有几个测试点通过了,系统也会给部分分。所以千万不要交白卷。你把暴力解法写上,把样例跑通,至少能拿一部分分数,比空在那里强得多。秋招笔试的通过线通常不是满分,而是排位制,一点一点的分值累积,可能就决定了你是进面试还是进人才池。
还有一点:如果编程题不会做,但能推断出某个测试点的答案,可以直接用“面向样例编程”的方式,比如判断输入为某个固定值时直接输出对应答案。这种方法当然不能保证全过,但在部分分评判机制下,有时候能多捞几分。我不鼓励这种投机,但秋招场上分数为王,心态上可以灵活一点。
4.3 赛码网提交的自测方法
代码写完以后,不要急着提交。先在本地或网页编辑器里自测三组数据:第一组用题目的样例,第二组用最小边界(比如N=0或N=1),第三组用极端大数值(判断溢出)。如果编程题要求输出整数,但中间过程可能超过int范围,一定要用long。
这里有个我自己踩过的坑:某个DP题的状态值在N=1000时不会溢出,但N=100000时中间值会超int,我没注意就WA了一次。后来养成习惯:凡是有累加、乘法、比较大小的题,统一用long做中间变量,基本无害,但能避免一类致命错误。另外,字符串输入用nextLine()读完一行后,如果还要继续读整数,建议在读完字符串后多写一句sc.nextLine()把换行符消费掉,否则会读到空串。
5. 避坑速查:平台、读题、边界、心态与复盘
5.1 我见过最多的失分点
第一个失分点是读题不仔细,把“最多”看成“最少”,把“非递减”看成“递增”,一个词选错,整个代码白写。建议把题目中的约束条件圈出来,尤其是数字范围和是否允许重复这两个点。第二个失分点是提交前没有删除调试输出,比如System.out.println("debug")直接打在正式输出里,系统会判错。第三个失分点是本地IDE能跑但平台报错,绝大多数原因是输入输出流没处理好或类名不匹配。
除了这三个,还有一个隐蔽的坑:题目里给的数据范围决定了算法复杂度上限。如果N是10^5,O(N^2)的解法大概率超时;如果N是100,理论上枚举也能过。很多人不管数据范围,套一个自己最熟的复杂度解法,结果在最后一个大数据用例上超时。拿到题以后,第一件事就是看N的范围,算出复杂度预算,再决定用暴力还是优化算法。
5.2 防止坏情绪影响后续题目的方法
笔试现场最怕的是前一道题卡住,导致后面的题全都心浮气躁。我的做法是:卡题超过15分钟,先深呼吸10秒,然后切换到基础选择题冷静一下,再回来继续。基础题里的知识点大多是自己熟悉的内容,做对几道以后,心态就回来了。这个方法听起来很玄,但实践下来真的很管用,紧张会让大脑短路,冷静下来以后很多思路会自动涌现。
另一个情绪管理技巧是提前给自己做好心理建设:笔试未必题题都会,遇到不会的很正常,你只需要把会的都做对。有了这个预期,就算压轴题没写出来,也不会崩盘。我见过心态崩掉的人,往往不是因为题目太难,而是因为“以为只有自己不会”,其实回头对答案时发现大家的情况差不多。
5.3 一场笔试后的动作比考试本身更重要
笔试交卷只代表一个阶段结束,真正的收获在于复盘。我会在笔试结束当晚把每道题重写一遍,记录自己当时的错误点和正确的解法,然后和同批次的同学对答案,看看自己是否遗漏了某些边界条件。这样一场笔试下来,即使最后没有进入面试,也相当于做了一次高强度的专项训练。秋招是一个长达几个月的长跑,很多人最后拿到的offer往往来自“复盘过的那家公司”,而不是“投简历最多的一批”。
复盘的具体做法很简单:每道题记录四件事——题目类型、你的解法、错误原因、最优解法。错题本不用整理得很精美,关键是第二天早上要重新看一遍。人的遗忘曲线很陡,你要是晚上复盘完直接睡觉,第二天就只剩下模糊印象;但如果第二天早上再过一遍,记忆会牢固得多。我靠这个方法,在后来的几家笔试里明显感觉到,凡是复盘过的知识点,再遇到时准确率都会提高一大截。
最后再分享一个我在多场秋招笔试里验证过的技巧:笔试前一晚不要刷难题,把模板题和错题本过一遍就睡觉。很多人觉得考前应该抓紧时间再刷几道题,其实睡眠不足会让手速和判断力同时下降,得不偿失。带着清晰的大脑上场,把会做的题稳稳拿下,这就是秋招笔试最务实的取胜之道。