Grab SDE编程面试LeetCode高频题型
一句话总结
大多数候选人把Grab SDE编程面试当成纯算法刷题比拼,失败的根本原因不是代码能力弱,而是误判了题目背后的系统设计意图。真正决定通过率的,不是你刷了多少LeetCode,而是能否在45分钟内将一道标准题型重构为可落地的工程问题。实际面试中,考树遍历的题,90%都藏着分布式ID生成或日志聚合的真实场景;表面是动态规划,实则是订单合并调度的建模简化。
不是你解不出最优解,而是你没看出面试官在等你问业务上下文。不是你写错边界条件,而是你跳过了对数据规模和并发量的确认。不是你时间复杂度算错,而是你没意识到面试官真正想听的是“如果这个逻辑上线,怎么扛住Grab打车高峰期每秒2万订单”。
最终进入offer阶段的候选人,不是LeetCode刷了1000题的人,而是能把第238题“除自身以外数组的乘积”讲成“如何在用户端预计算优惠券叠加影响”的人。代码只是载体,背后对系统规模、故障容忍、灰度发布的理解,才是Grab工程文化真正筛选的东西。
适合谁看
这篇文章适合三类人:第一类是正在准备Grab新加坡或越南SDE岗位的候选人,尤其是从国内大厂跳槽、习惯纯算法突击的程序员。你可能刷了600+ LeetCode,但在最近一次Grab电话面试中栽在一道看似简单的BFS题上——不是因为不会写,而是面试官追问“如果这个图是全东南亚的司机位置实时拓扑,你怎么设计缓存和更新频率”时,你愣住了。
第二类是卡在Onsite最后一轮的候选人。你顺利通过了前两轮编码轮,却在System Design轮被Hiring Manager问住:“你刚才写的订单匹配算法,如果同时有10万乘客发起叫车,每个匹配尝试涉及3次数据库查询,这个服务的P99延迟会崩到多少?”你给出了估算,但没提异步批处理或本地缓存,最终收到“技术深度不足”的反馈。
第三类是误以为Grab技术标准低于美国FAANG的人。你听说Grab用的LeetCode难度以Medium为主,于是只准备了模板题,结果在Onsite第三轮遇到一道“出租车路径合并优化”,题目编号在LeetCode上甚至不存在——它本质是带权区间调度的变体,但面试官要求你考虑司机空驶成本、乘客等待心理阈值、平台抽成动态调整三个维度。
你不是题目不会,而是没意识到Grab的编程题从来不是孤立的。每一道题都是一个微缩版系统问题。这篇文章的价值,就是帮你把“会做题”升级为“会判断题的真正目的”。
编程面试到底在考什么:算法表象下的工程决策
Grab的编程面试从不是纯算法竞技场。如果你把它当成LeetCode分类训练营来准备,第一轮电话面试就会被淘汰。真正的考察点,不是你能否写出最优解,而是你能否在解题过程中暴露工程思维。
一场典型的SDE I电话面试从面试官共享文档开始:“我们现在有一个司机接单推荐系统,给定一组乘客请求P和一组司机位置D,如何快速匹配最近的司机?”这道题表面上是最近点对问题,LeetCode 220或242的变体,但核心陷阱在于数据规模和更新频率。
面试官不会主动告诉你数据量。你必须主动问:“这个匹配是实时触发还是批量处理?每秒大概有多少请求?”正确答案不是“我假设1000QPS”,而是追问:“Grab在吉隆坡高峰期每秒处理约8000订单,这个系统是为特定城市设计,还是需要支持多区域并发?
”——这才是Grab工程师日常讨论的语境。我曾参与一次hiring committee debrief,一名候选人用KD-Tree解了二维空间搜索,代码无误,时间复杂度O(log n),但被拒。理由是:“他没有意识到实时位置数据写入频繁,KD-Tree的构建成本远高于查询收益,Z-order曲线+分片才是生产环境方案。”
不是你在算法层面错了,而是你忽略了系统约束。不是你代码风格不规范,而是你跳过了对一致性的讨论。不是你没写test case,而是你没提“如果司机位置更新延迟200ms,匹配结果是否可接受?”——这在Grab的打车场景中是生死问题。
另一个insider场景来自2023年Q2的一次Onsite debrief。候选人解了一道“乘客取消订单预测”的DP题,状态转移方程正确,但面试官追问:“如果这个模型每天要处理5TB日志,你如何设计数据管道?”候选人回答“用Spark批处理”,面试官继续:“如果我们要实时调整取消阈值,基于前5分钟数据动态更新,怎么办?
”候选人卡住。最终评价是:“具备基础编码能力,但缺乏将算法嵌入数据流的意识。”——这正是Grab区别于纯刷题公司的关键:代码必须可部署。
真正的判断标准是:你写的每一行代码,是否能直接贴到Grab的Go服务里跑起来。这意味着你要考虑:输入是否可能为null,字符串编码是否UTF-8,整数溢出在东南亚用户ID上是否可能发生(用户ID已超2^31)。
你不是在写LeetCode提交代码,而是在写production代码。因此,准备方向必须从“背题”转向“重构题”——把每道题都当成一个微服务接口来设计。
高频题型背后的业务映射:从LeetCode到Grab生产系统
Grab的LeetCode高频题从来不是随机抽取。它们是生产问题的高度抽象。以“滑动窗口最大值”(LeetCode 239)为例,这道题在Grab至少关联三个真实系统:实时定价波动监控、司机行为异常检测、订单取消率窗口统计。
面试中若只实现deque方案,顶多拿勉强通过。但如果你在写完基础解法后说:“在Grab的监控系统中,这个窗口可能跨机房,我们需要用Kafka Streams的Session Window来处理乱序事件”,面试官眼神会立刻变化。
真实场景发生在一次Onsite面试中。候选人被要求实现“找出数组中前K个高频元素”(LeetCode 347)。他用最小堆实现O(n log k),面试官点头,但接着问:“如果这个数组是GrabPay每分钟交易品类统计,数据来自10个微服务,每个服务上报自己的部分频次,你怎么合并?
”候选人改用Count-Min Sketch,提到“可以容忍少量误差,换取内存和网络开销降低”,并举例“类似我们处理用户点击流的方案”。这一句话让他从“可能通过”变成“strong hire”。
不是你算法复杂度算得准,而是你能否识别题目的业务投影。不是你用了先进数据结构,而是你能否说明为什么它适合分布式环境。不是你代码跑通了测试用例,而是你能否预判线上故障场景。
另一道高频题是“岛屿数量”(LeetCode 200)。在面试中,它常被包装为“识别连续异常交易区域”。解法是DFS/BFS,但关键在后续讨论。面试官会问:“如果这个矩阵是实时更新的,每秒有数千个交易写入,你怎么保持图的连通性?”正确路径是转向Union-Find,并讨论“如何批量处理更新以减少锁竞争”。若候选人只停留在递归实现,几乎必挂。
还有一个被低估的题型是“设计Twitter”类系统设计题的编码子任务,比如“合并K个有序链表”(LeetCode 23)。这直接对应Grab Feed流的多源内容聚合。面试官希望听到你讨论“如何避免饥饿问题——低频更新的服务是否永远无法展现在顶部”,进而引出weighted merging或exponential backoff polling。
具体到数据,Grab近一年SDE I/II Onsite中,出现频率最高的五类题型是:1)区间合并与调度(涉及订单打包);2)图的连通性与最短路径(司机路线优化);3)滑动窗口与流处理(实时指标计算);4)前K大/小问题(排行榜与推荐);5)字符串匹配与模糊搜索(司机车牌识别OCR后纠错)。每一道,都有对应的生产服务支撑。
准备这些题的正确方式,不是背模板,而是反向工程:找到Grab Engineering Blog中提到的系统,如“Real-time Surge Pricing”,然后思考“哪个LeetCode题可能是其核心算法模块的简化版”。你不是在猜题,而是在重建技术决策链。
面试流程拆解:每一轮的隐藏评分标准
Grab SDE面试流程共四轮,每轮45分钟,全部远程。第一轮是电话筛选,由L4或L5工程师主持,考察基础编码与调试能力。题目通常是LeetCode Medium,如“验证二叉搜索树”或“反转链表”。但评分重点不是代码是否正确,而是你是否在写之前确认输入约束。
例如,面试官给TreeNode类,你必须问:“val是否可能为负?树高是否可能超过10^4导致递归栈溢出?”——在Grab的Go服务中,递归深度超过1000就视为风险。
第二轮是Onsite第一轮,侧重系统编码(System-focused Coding)。题目如“实现一个支持过期的LRU Cache”,但面试官会逐步增加约束:“如果这个Cache要存储10TB用户会话数据,如何分片?”“如果Redis宕机,本地Cache如何保证最终一致性?
”——这轮考察的是你能否在编码中融入容错设计。我参与过一次debrief,候选人用LinkedList+HashMap实现基础LRU,代码正确,但被拒。原因是:“他没有提到并发场景下ReadWriteLock的开销,也没有考虑GC对大对象的影响,这在高QPS服务中会导致毛刺。”
第三轮是算法深度轮,题目往往是Hard级变体,如“在加权图中找K条最短路径”用于备选司机推荐。关键不是写出Yen's Algorithm,而是你能简化假设:“在Grab场景中,K通常小于5,我们可以用A剪枝,牺牲完备性换速度。”这种基于业务的权衡决策,比纯算法实现更重要。
第四轮是Hiring Manager轮,表面是行为面试,实则嵌入编码问题。如“描述你最复杂的项目”,当你讲完,面试官会说:“假设我们要把这个系统移植到Grab的东南亚多语言环境,你会如何修改数据模型?”——这轮考的是技术领导力与跨文化系统适配能力。
每轮通过率约30%。最终offer由Hiring Committee集体决定,标准不是“每轮都好”,而是“至少一轮表现出突出工程判断力”。薪资方面,SDE I 新加坡 base $120K,RSU $80K/年(分4年归属),bonus 10-15%;
SDE II base $160K,RSU $120K/年,bonus 15-20%。总包对标FAANG但略低,但工作强度也相对可控。
如何准备高频题型:从刷题到重构题
准备Grab的编程题,正确路径不是按Tag刷题,而是按“业务场景”重构题库。例如,将“动态规划”类题分为:1)订单合并调度(区间DP);2)路径成本优化(网格DP);3)资源分配(背包变体)。
每类对应Grab的真实系统。以订单合并为例,题目可能是“给定N个乘客请求,每个有出发地、目的地、时间窗,如何最大化合并率?”——这本质是LeetCode 1235“规划兼职工作”的变体,但增加了空间维度。
准备时,必须为每道题建立“生产映射表”。例如LeetCode 189“旋转数组”,在Grab可能用于“司机值班表轮转”或“促销活动周期配置”。你不仅要写三次反转解法,还要能说出:“在微服务配置中心,我们会用ZooKeeper Watcher监听rotation周期变更,并触发滚动重启。”
具体操作上,每天准备2道题,但每道花3小时:1小时写代码,1小时找Grab相关系统(如阅读Grab Tech Blog),1小时模拟面试——不是自言自语,而是找有Grab背景的人对练。关键是在解完题后主动加戏:“如果这个逻辑要部署到Kubernetes,我会怎么设置readiness probe?”
另一个重点是语言选择。Grab主要用Go和Java。用Python面试虽允许,但会被认为“不适合生产环境”。Go的优势在于并发原语(goroutine, channel)和内存控制,这在司机调度服务中至关重要。例如写BFS时,用channel控制并发goroutine获取邻居节点,比单纯用queue更贴近实际。
准备清单中必须包含:1)重写LeetCode模板,加入error handling和context deadline;2)为Top 50高频题每道写一段“如果上线”的风险说明;3)模拟面试时强制自己先问三个问题:“数据量级?更新频率?失败容忍度?”——系统性拆解面试结构(PM面试手册里有完整的[Grab技术栈实战复盘]可以参考)。
最终目标不是“我会做这道题”,而是“我知道Grab为什么考这道题”。
准备清单
- 重写所有LeetCode模板代码,加入生产级健壮性:每道题的提交代码必须包含输入校验、边界处理、错误返回。例如写二分查找时,必须处理start > end、target超出范围等情况,并用Go的error类型显式返回,而不是只返回-1。
- 建立高频题业务映射表:为LeetCode前50道Grab高频题,每道标注至少一个真实应用场景。例如“拓扑排序”对应“服务依赖启动顺序”,“最小生成树”对应“跨城司机调度网络优化”。在面试中主动提及,展示技术联想力。
- 掌握Go并发原语在算法中的应用:在写BFS、DFS时,能自然使用goroutine和channel。例如“我们可以启动多个goroutine并行处理不同子树,用WaitGroup同步”,这比单线程递归更贴近Grab的高并发环境。
- 模拟面试必须包含追问环节:找同伴练习时,要求对方在你解完题后提出系统级问题:“如果数据量增大100倍怎么办?”“如何监控这个函数的P99延迟?”——提前适应Grab的深度追问文化。
- 研究Grab Engineering Blog至少10篇:重点关注“Real-time Data Processing”、“Microservices at Scale”、“Go in Production”等主题。从中提取技术关键词,如Kafka Streams、gRPC、Istio,在面试中自然引用。
- 准备三个“失败改进”案例:每个案例包含:原方案(如单机Cache)、问题(内存溢出)、改进(分片+Redis Cluster)、结果(QPS从5k提升到50k)。在Hiring Manager轮展示技术演进思维。
- 系统性拆解面试结构(PM面试手册里有完整的[Grab技术栈实战复盘]可以参考):将每轮面试视为一个产品需求,明确输入(题干)、约束(时间/空间)、输出(可部署代码)。用产品思维重构解题流程。
常见错误
错误一:只实现最优解,不讨论权衡
BAD:面试官问“如何找数组中位数”,候选人直接写快速选择算法,O(n)时间,代码正确。
GOOD:候选人先问数据规模,得知“10亿条日志”后说:“快速选择理论上O(n),但随机pivot在生产环境可能导致毛刺,我建议用近似算法如T-Digest,牺牲精度换稳定P99。”——后者展示了工程判断。
错误二:忽略语言特性与生产环境匹配
BAD:用Python写LRU Cache,使用dict和list,pop(0)操作O(n)。面试官问“高并发下性能?”候选人答“用OrderedDict”。
GOOD:用Go实现,使用map[key]Node + doubly linked list,并说明:“我用sync.RWMutex控制读写,读多场景下性能更好。另外,Go的逃逸分析确保Node在堆上分配,避免GC频繁触发。”——紧扣生产实践。
错误三:不主动暴露系统思维
BAD:解完“合并区间”后,主动结束:“代码写完了,您看还有什么问题?”
GOOD:解完后补充:“在Grab的订单系统中,区间是用户乘车时间段,合并意味着拼车。我需要考虑时区转换——新加坡和曼谷时间差,以及司机接单确认延迟导致的区间扩展。”——将题拉回业务,创造加分点。
准备拿下PM Offer?
如果你正在准备产品经理面试,PM面试手册 提供了顶级科技公司PM使用的框架、模拟答案和内部策略。
FAQ
Q:Grab的编程题难度和FAANG比如何?是否真的以Medium为主?
A:Grab的题目编号多为Medium,但考察深度远超多数人预期。例如一道标为Medium的“二叉树垂直遍历”(LeetCode 314),在Grab面试中会延伸:“如果这棵树是司机组织架构,每层代表区域经理,如何优化查询某大区所有司机的效率?”——这瞬间变成分布式索引设计问题。我参与过HC讨论,一名候选人用哈希表+DFS完成基础遍历,代码正确,但被拒。
理由是:“他没有意识到树可能深达20层,递归会导致goroutine栈溢出,应改用BFS+队列”。另一个案例:LeetCode 56“合并区间”被用于司机排班系统,面试官追问:“如果区间更新频繁,如何设计增量合并算法?”——这已是研究级问题。因此,不能以题目难度标签判断准备强度,必须按“可能延伸至系统设计”的标准对待每道题。
Q:是否必须用Go?Python会不会直接被刷?
A:不会直接刷,但会显著降低评价等级。在2023年一次debrief中,两名候选人解同一道“最小路径和”题,一人用Go,一人用Python。Go候选人实现时加入context.WithTimeout(time.Millisecond*100),并说明“防止DB查询阻塞太久”;Python候选人用纯数组操作。两人代码都正确,但Go候选人被评为“production-ready”,Python者为“academic correct”。
最终前者过,后者挂。Grab内部服务90%用Go,面试官期待你用语言特性体现工程意识。例如,用defer处理资源释放,用channel做并发控制。用Python虽能过早期轮次,但在HM轮会被质疑“能否快速融入团队”。建议:至少掌握Go基础语法和并发模型,面试时优先使用。
Q:RSU发放是新加坡股还是美国股?归属时间是否有特殊条款?
A:Grab的RSU是开曼群岛注册实体的股票,通过ESOP计划发放,与美国上市股同源。SDE I/II岗位RSU分4年归属,每年25%,无cliff。例如年RSU $80K,则每年归属$20K价值股票。但需注意:Grab股价未公开,流动性差,变现依赖公司回购或未来上市。
在HC讨论中,有候选人因“对RSU理解不清”被扣分——当被问“如何看待薪酬结构”时,回答“RSU和Cash一样”被视为缺乏财务意识。正确回答应是:“我理解RSU是长期激励,与公司成长绑定,我关注的是技术挑战而非短期变现。”薪资构成上,base $120K-$160K,RSU $80K-$120K/年,bonus 10-20%,总包SDE I约$230K-$260K,SDE II $300K-$380K。该结构鼓励长期投入,筛选出真正认同Grab使命的工程师。
准备好系统化备战PM面试了吗?
也可在 Gumroad 获取完整手册。