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

资讯详情

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

PayPal软件工程师实习笔试全解析:题型考点与编程题复盘

PayPal软件工程师实习笔试全解析:题型考点与编程题复盘 2017年春季我投了PayPal上海研发中心的暑期实习岗位。简历筛过之后收到的是一封带有在线笔试链接的邮件考试时长90分钟试卷代号是“软件工程师B卷”。当时同一批笔试的同学里有人拿到的是A卷题目不完全一样但结构和难度基本对齐。这套卷子给我的最大感受是它不考偏题怪题却能把一个人的工程基础扎不扎实测得很清楚。今天把这套卷子的题型分布、考点解析、三道编程题的做法以及复盘后的备考方法完整梳理一遍给准备投PayPal或同类外企软件工程师实习的同学一份可参考的路线图。1. 笔试全景平台、题量与B卷的总体难度先说考试环境。外企技术岗的线上笔试大多跑在HackerRank这类在线评测平台上PayPal用的是同一套玩法浏览器打开代码编辑器写完直接提交系统自动编译并跑测试用例。你可以在本地IDE里调试但最终以网页端的提交为准所以平时的编码习惯——比如能不能不靠IDE自动补全写出完整代码——这时候就会被放大。整份B卷的题量结构大致如下表模块题量建议用时分值占比计算机基础选择题30题左右35分钟约40%编程题一字符串处理1题12分钟约15%编程题二链表/树操作1题20分钟约25%编程题三动态规划1题15分钟约20%注意编程题的计分不是“要么全对要么零分”而是按通过的测试用例给分。系统里藏着几组你没见过的测试用例专门用来抓边界条件。这导致一个很现实的情况代码逻辑主体对了但漏了空输入判断一样会扣分。B卷和A卷的关系也值得说一句。它们是从同一个题库里抽题生成的平行卷难度对齐但题目不同目的是降低抄袭和泄题的影响。所以网上有人说“背下A卷答案就能过B卷”这种想法基本不成立你顶多能押中题型押不中具体题目。这90分钟的节奏其实很紧。30道选择题如果每题磨蹭2分钟就占掉一个小时编程题只剩半小时基本写不完。我当时给自己定的策略是选择题每题不超过1分钟拿不准的先标记跳过等编程题写完剩下时间再回来纠结。这个策略在后面验证是有效的因为编程题的分值密度远高于选择题。2. 选择题表面考基础实际在考工程底线选择题一共30道左右覆盖数据结构、算法、Java语言、并发、网络、数据库几个方向。看起来都是“本科专业课必背知识点”但PayPal的出题角度比国内一些公司更刁——它不问你“什么是哈希表”而是给你四个方案让你判断哪个不是哈希冲突的解决方法或者给一段多线程代码让你分析输出结果。2.1 数据结构与算法考点一半题目都落在这哈希表是绝对高频考点。B卷里有一道题以下哪种方法不是解决哈希冲突的常用手段选项是开放定址法、链地址法、再哈希法、二分查找。答案是二分查找。这道题本身不难但它背后有个延伸问题Java的HashMap在JDK 1.8里链表转红黑树的阈值为什么是8这牵扯到泊松分布——在负载因子0.75的默认配置下哈希桶里链表长度达到8的概率极低大约千万分之一所以用红黑树应对极端哈希碰撞就够了。这种“知道结论还知道为什么”的深度恰好是外企笔试和国内笔试的一个典型差异。栈和队列也考了一道经典题给定一个入栈序列1到5问以下哪个出栈序列是不可能出现的。这种题考的是栈的LIFO特性画个草稿模拟一下就能排除。二叉树那边出现了“已知前序遍历和中序遍历求后序遍历”的题目解题关键是用前序的第一个元素在中序里定位根节点然后递归处理左右子树。这道题在2017年多份外企笔试卷里都出现过建议直接背熟递归写法。排序算法考的是复杂度边界快速排序在最坏情况下的时间复杂度是多少答案是O(n²)——当每次划分都选出最小或最大元素作为基准时递归深度变成n每层比较n次。很多人只记得快排平均O(n log n)忽略了最坏情况这就是选择题埋坑的典型手法。2.2 Java语言特性与多线程PayPal后端的主语言视角PayPal的后端大量使用Java所以B卷里Java相关题目占比不低。有一道很经典两个线程同时对volatile修饰的int变量执行i操作各10000次最终结果是否一定等于20000答案是否定的。volatile只保证可见性不保证原子性i是“读-改-写”三步操作两个线程可能同时读到同一个旧值导致丢更新。正确做法是AtomicInteger或synchronized。这道题放到支付场景里非常实际——账户余额的并发更新如果处理不好就是真实的生产事故。GC相关考了一道“以下哪些对象可以作为GC Roots”的多选正确选项包括栈帧中的局部变量、静态变量、常量引用、JNI引用。这道题考的是JVM内存回收的起点概念属于《深入理解Java虚拟机》里最基础的内容但没认真看过JVM的同学容易漏选。线程池也出现了问的是ThreadPoolExecutor的核心参数关系corePoolSize、maximumPoolSize、workQueue三者如何配合。标准流程是任务进来先判断当前线程数是否小于corePoolSize小于则创建核心线程执行大于则入队队列满了才创建非核心线程直到maximumPoolSize再满就触发拒绝策略。四种拒绝策略里AbortPolicy是默认的直接抛异常。2.3 网络与数据库支付系统跑不掉的底层依赖网络题里必有一道HTTP方法幂等性判断。GET、PUT、DELETE是幂等的POST不是。为什么会考这个因为支付系统里的重试机制几乎完全依赖幂等设计——客户端超时重试时服务端必须能识别出“这是同一笔请求”否则用户会被重复扣款。这个知识点在笔试里才占1分但到了面试环节面试官会顺着它追问“你怎么设计一个幂等接口”“幂等键存哪里”“用Redis还是数据库唯一索引”。所以笔试遇到它别只记答案把背后的系统设计逻辑也过一遍。数据库考了一道联合索引最左前缀原则表里有联合索引(a, b, c)以下哪个查询条件能用到这个索引答案是a或ab或abc组合的查询只查b或只查c的情况下索引失效。另一道是LIKE查询SELECT * FROM t WHERE name LIKE %abc%不会走name索引因为前导通配符导致无法利用B树的有序性。事务隔离级别也来了一道问“可重复读”隔离级别下能否避免幻读。MySQL默认的InnoDB引擎在可重复读下通过MVCC多版本并发控制解决了快照读的幻读问题但当前读加锁的SELECT ... FOR UPDATE仍然可能出现幻读。这个细节很多刷题网站都讲不清建议专门花半小时把MVCC的读写视图机制捋一遍。3. 三道编程题分层设计第三题才是分水岭三道编程题的难度是明显递进的。第一题只要仔细就能拿全分第二题考经典算法第三题拉开差距。这种设计是有意的——笔试筛选的目标不是满分选手而是能稳定拿到80分以上的人。3.1 第一题字符串游程编码送分但暗藏边界题目大意实现一个字符串压缩函数把连续相同的字符压缩成“字符出现次数”。例如输入aabcccccaaa输出a2b1c5a3。如果压缩后的字符串长度不小于原串则返回原串。这题我给出的解法public static String compress(String s) { if (s null || s.length() 2) { return s; } StringBuilder sb new StringBuilder(); int count 1; for (int i 1; i s.length(); i) { if (i s.length() s.charAt(i) s.charAt(i - 1)) { count; } else { sb.append(s.charAt(i - 1)).append(count); count 1; } } String compressed sb.toString(); return compressed.length() s.length() ? compressed : s; }这道题的翻车点非常典型。第一忘了处理字符串末尾的连续字符——循环结束前最后一组字符还没写入StringBuilder我当年就在这丢了两个测试用例。第二没有判断空字符串和单字符输入。第三忘了比较压缩后和压缩前的长度导致“aabbcc”这种压缩后一样长的字符串被错误返回成了压缩版本。第四用字符串拼接而不是StringBuilder在长输入下性能堪忧。提示笔试里遇到字符串题先在草稿纸上列出“空串、单字符、全相同字符、无重复字符、末尾连续字符”这五类用例再开始写代码。3.2 第二题链表判环并返回环的入口题目给定单链表头节点判断链表是否有环如果有返回环的入口节点。要求空间复杂度O(1)。这是Floyd判圈算法的经典应用分两步快慢指针在环内相遇后把快指针重置到头节点然后两个指针每次都走一步再次相遇的位置就是环入口。public static ListNode detectCycle(ListNode head) { if (head null || head.next null) { return null; } ListNode slow head; ListNode fast head; while (fast ! null fast.next ! null) { slow slow.next; fast fast.next.next; if (slow fast) { break; } } if (fast null || fast.next null) { return null; } fast head; while (slow ! fast) { slow slow.next; fast fast.next; } return slow; }很多人会用这种方法但说不清为什么。面试追问时你要能推导设头节点到环入口的距离为a入口到相遇点的距离为b环剩余长度为c。相遇时慢指针走了ab快指针走了abk(bc)。因为快指针速度是慢指针的两倍所以2(ab)abk(bc)化简得到a(k-1)(bc)c。这说明从头节点出发的指针走a步到达环入口时从相遇点出发的指针也恰好走完c加整圈回到环入口。这个数学推导是这道题真正的区分点只背代码不理解原理面试官一问就露馅。3.3 第三题硬币找零最小硬币数题目给定硬币面额数组coins和一个总金额amount每种硬币数量不限返回凑成amount所需的最小硬币数如果无法凑出返回-1。这道题放在PayPal的试卷里很应景——找零、清结算、账户余额核算本质上都是这类组合优化问题。标准解法是动态规划public static int coinChange(int[] coins, int amount) { int[] dp new int[amount 1]; Arrays.fill(dp, amount 1); dp[0] 0; for (int i 1; i amount; i) { for (int coin : coins) { if (coin i) { dp[i] Math.min(dp[i], dp[i - coin] 1); } } } return dp[amount] amount ? -1 : dp[amount]; }代码核心就三句话dp[i]表示凑出金额i需要的最少硬币数初始化成amount1当作“不可达”dp[0]0作为边界。状态转移方程是dp[i] min(dp[i], dp[i - coin] 1)遍历所有面额取最小值。时间复杂度和空间复杂度分别是O(amount×硬币种类数)和O(amount)。这道题的关键在于初始化。把不可达状态设成amount1而不是Integer.MAX_VALUE是一个很实用的工程技巧——避免在dp[i - coin] 1时发生整数溢出。很多人在这一题上不是不会动态规划而是被“不可达状态怎么表示”这种细节绊倒。另外提一句这题也可以用BFS或DFS剪枝做但在笔试限时场景下DP是最好写、最容易验证正确性的方案。除非你能证明贪心策略在该面额组合下成立比如面额是倍数关系否则别在笔试里用贪心硬币找零的贪心解法在面额不规整时是错的。4. 为什么考这些从笔试题反推PayPal的工程师画像把整套卷子过完你会发现它的考点选择不是随机的。任何一个做过支付系统的人都能看懂这套出题逻辑。4.1 支付领域的技术约束如何映射到考题支付系统对“正确性”的要求极端苛刻所以整张卷子从头到尾都在测一件事你能不能写出在各种边界条件下仍然正确的代码。字符串压缩考的是空输入和末尾字符链表判环考的是空链表和单节点硬币找零考的是不可达状态——这些都是真实业务代码里天天要面对的问题。账户余额、订单状态、交易流水任何一个字段的取值错误都是钱的问题。并发考题多也是同一个原因。一个账户的余额可能被多个请求同时扣减这种并发场景在银行和支付公司里太常见了。笔试里的volatile题和线程池题放到业务里就是“并发扣款怎么保证不超扣”“线程池参数怎么配才不会被流量打垮”。PayPal面试官想看的是你不仅知道这些概念还能把它们和真实系统的风险点对上号。哈希表的高频出现同样有迹可循。支付系统的核心链路里按订单号查缓存、按用户ID做路由、分布式缓存的分片全部建立在哈希算法之上。面试官不会要求一个实习生懂一致性哈希的虚拟节点细节但你需要理解哈希表的基本原理否则后续系统设计的讨论根本没法展开。4.2 笔试之后这道题在面试里会被追问成什么样子2017年这轮笔试通过后我经历了后续的面试。复盘时发现笔试里几乎每道题都能在面试中被延伸成一轮独立的考察。笔试考了HashMap面试就问“JDK 1.8和1.7的HashMap有什么变化”“为什么线程不安全”“ConcurrentHashMap的锁粒度是怎么优化的”。笔试考了TCP三次握手面试就问“为什么不是两次或四次”“SYN Flood攻击怎么防御”。笔试考了硬币找零面试就问“如果硬币面额变成了纸币张数受限怎么办”“这个DP能不能优化空间”。所以我的建议是不要把笔试当终点把它当一次开卷摸底。每做一道题顺手把这道题所有的扩展方向都想一遍。笔试分数决定你有没有面试资格但面试里你能走多远取决于你笔试后有没有把那些“为什么”补上。5. 复盘之后给下一届考生的备考清单5.1 时间分配别在选择题上恋战90分钟要做30道选择题加3道编程题时间是很紧的。我的实测分配方案是选择题35分钟内全部过一遍超过1分钟没思路的直接在答题卡上标记跳过编程题按顺序做第一题12分钟第二题22分钟第三题18分钟最后留5分钟统一检查编译错误和遗漏的边界条件。如果编程题做到一半卡住了先写一个能过部分用例的暴力解法把基础分拿到再回头优化。在在线评测系统里暴力解通常能拿到30%到50%的用例分这比交白卷强太多。5.2 最容易翻车的几个点第一输入为空或单元素没处理。这是编程题扣分的头号原因字符串题尤其严重。第二整数溢出。金额相关的计算场景里用int存累加结果分分钟溢出关键变量用long。第三没有仔细读题题目要求“压缩后长度不小于原串则返回原串”漏掉这个判断就会在特定用例上出错。第四在遍历集合的同时修改集合触发了Java的fail-fast机制ConcurrentModificationException一抛整题零分。第五笔试平台的语言版本和本地不一样比如Java 8和Java 11里一些API行为有差异提交前先在网页端的编译器里跑一遍。5.3 考前一周的实操清单如果只剩一周别盲目刷题海按下面的清单做效率最高LeetCode前200题里的easy和medium题目过一遍重点看字符串、链表、二叉树、动态规划四类。手写一遍快排、二分查找、链表反转、二叉树前中后序遍历要求不查资料、一次写对。把HashMap、HashSet、ArrayList、LinkedList的常用API及其时间复杂度背熟笔试时不需要纠结“这个方法叫什么名字”。复习Java并发基础synchronized和volatile的区别、线程池五个参数、wait和sleep的区别、常见的死锁场景。网络和数据库各花半天三次握手四次挥手、HTTP幂等性、索引失效场景、事务隔离级别。我个人在实际操作中的体会是这套卷子考的不是你刷了多少题而是你在有压力的环境下能不能写出干净、健壮的代码。当年我做字符串压缩题时因为忘了处理末尾字符白白丢掉了两个测试用例的分——这种错误在本地调试时一分钟就能发现但笔试时一紧张就漏了。所以我现在带实习生第一件事就是让他们养成写代码前先列边界条件清单的习惯。PayPal这道笔试已经过去几年了但它“基础判断题加分层编程题”的筛选思路至今仍然是很多外企技术岗筛实习生的标准模板。把这套逻辑吃透比盲目刷三百道题有用得多。
返回列表