ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

BAT实习笔试复盘:算法与系统设计高频考点解析

BAT实习笔试复盘:算法与系统设计高频考点解析 1. 考情复盘这份试卷到底在筛选什么1.1 2015年实习内推笔试的真实定位先聊个背景。2015年那会儿移动互联网正处于快速上升期BAT三家的实习内推是很多学生挤破头想争取的机会。所谓内推本质上是用人部门和HR提前锁定一批有潜力的人笔试环节不像校招那样只求基准分而是通过一套统一卷子在短时间内判断你有没有工程潜力、能不能接住后面几轮面试的压力。所以当你拿到这份“BAT 2015实习内推笔试卷第二场”的时候我的第一建议是别急着埋头刷题先把出题人的逻辑想明白。这份卷子的定位非常明确不考死记硬背考的是限定时间内的综合反应。“第二场”这个概念说明笔试是分批进行的题目在同一知识框架下有轮换场次之间难度会有一点波动但核心考察面非常稳定——算法与数据结构是绝对的主战场系统设计、语言基础、网络和操作系统则是拉开差距的分水岭。整场笔试的筛选比例通常很夸张大量人会在基础题上摔跟头而这些基础题恰恰不区分专业背景只区分你有没有认真准备过。1.2 题型结构与时间分配单题限时的残酷现实从我复盘多份同类试卷的情况来看BAT实习笔试通常由三部分构成第一部分是选择题/填空题覆盖C/Java、操作系统、计算机网络、数据库基础第二部分是2到3道编程大题以算法与数据结构的经典题为主第三部分是简答题或小型系统设计题一般给出一个具体场景让你写方案、画数据结构、甚至给出表结构设计和接口定义。题量看起来不大但时间卡得很死。我当时实测整卷建议用时120分钟编程题部分平均每道题只有25到30分钟意味着你需要直接写出可运行的、边界条件完整的代码而不是先写伪代码再慢慢完善。很多人挂在编程题上不是因为不会而是因为时间分配失衡——前面选择题纠结太久后面编程题仓促提交。我建议的时间分配是选择题40分钟编程题70分钟系统设计题10分钟整理思路。如果某道选择题超了3分钟还没把握直接凭第一感觉选做完标记回头再想。题型题量建议用时期望得分选择题/填空15-20题40分钟80%以上编程大题2-3题70分钟至少完成2题系统设计/简答1题10分钟写出完整框架2. 算法与数据结构用真题思路拆解高频考点2.1 动态规划与字符串处理的一题多解字符串类动态规划几乎是BAT笔试的固定嘉宾。让我印象最深的一类题是“最长回文子串”这道题在2015年前后的出现频率非常高考察点不是你会不会背状态转移方程而是能不能在边界条件上不出错。最稳妥的解法是中心扩展法时间复杂度O(n^2)空间复杂度O(1)。思路很简单任何一个回文串都关于中心对称枚举每个中心位置向两侧扩展记录最长的长度。需要注意区分奇数长度和偶数长度两种情况代码里要写两个扩展入口。当时我有个小技巧把字符串预处理成“#a#b#c#”的形式这样可以统一处理奇偶虽然会增加一点空间但代码逻辑会简洁很多。class Solution { public: string longestPalindrome(string s) { int n s.length(); if (n 2) return s; int start 0, maxLen 1; for (int i 0; i n; i) { int len1 expand(s, i, i); // 奇数长度中心 int len2 expand(s, i, i 1); // 偶数长度中心 int len max(len1, len2); if (len maxLen) { start i - (len - 1) / 2; maxLen len; } } return s.substr(start, maxLen); } int expand(const string s, int left, int right) { while (left 0 right s.length() s[left] s[right]) { left--; right; } return right - left - 1; } };笔试中这道题的坑不在算法本身而在输入为空的边界判断以及left和right指针同时减小时是否越界。我见过不少同学把left 0写成left 0导致整个测试样例直接报错。这种细节在笔试评分里很致命因为线上判题系统只会给你“通过/未通过”的结果不会告诉你错在哪一行。2.2 链表与二叉树笔试爱出但容易翻车的两类题链表反转是另一个高频题让我比较意外的是很多人背了递归写法却忘了迭代写法。面试官和笔试判题系统更偏好迭代实现因为递归版本在链表很长时有栈溢出风险而且可读性并不比迭代好。迭代的核心就是三个指针pre、cur、next每次把cur的next指向前一个节点然后整体向后移动。ListNode* reverseList(ListNode* head) { ListNode* pre nullptr; ListNode* cur head; while (cur ! nullptr) { ListNode* next cur-next; cur-next pre; pre cur; cur next; } return pre; }二叉树这边层序遍历是必考项。2015年的卷子喜欢在层序遍历上做变体比如“Z字形层序遍历”——奇数层从左到右偶数层从右到左。最直接的写法是用双端队列或者用普通队列存储后根据层号判断是否需要反转。我在实际笔试里选择的是后者因为代码量少、不容易出错虽然多一次reverse的开销但在O(n)复杂度下完全可接受。二叉树题目的另一个容易翻车的点在广度优先搜索时忘记记录每层节点数。很多人用队列直接pop到为空结果输出的是一维数组而不是分层的结果。正确做法是进入每层前先记录当前队列长度只处理这个长度的节点。2.3 二分与排序变体容易被忽略的隐藏分除了上面两类二分查找的变体题在第二场卷子里也出现过。我记得一道“旋转排序数组中的最小值”题目背景是一个有序数组在某个未知位置打了转要求O(logn)找最小值。这题看着简单但二分条件的推导很容易出错。核心思路是拿中间元素和右端点比较如果nums[mid] nums[right]说明最小值在右半部分left mid 1否则最小值在左半部分或就是mid本身right mid。这里有个容易踩的坑就是到底是用left right还是left right作为循环条件。我用下来最顺手的是left right因为当两者相遇时位置就是答案不需要额外判断。int findMin(vectorint nums) { int left 0, right nums.size() - 1; while (left right) { int mid left (right - left) / 2; if (nums[mid] nums[right]) { left mid 1; } else { right mid; } } return nums[left]; }这种变体题是试卷里性价比很高的部分因为只要记住比较逻辑就不会错但很多人没有专门练过考场上容易绕进去。我建议在准备阶段把二分模板统一成“闭区间搜索答案”风格所有变体题都用同一套循环条件而不是每道题换一种写法。3. 系统设计题高并发场景下怎么组织答案才有分3.1 短URL系统从62进制到全局ID生成器系统设计题在2015年的实习笔试中已经出现虽然不会像社招那样要求画完整架构图但会问一些“给方案”“给数据结构”“给核心接口”的问题。其中非常经典的一道是短URL系统设计题目大概是微博/内部系统需要把长链接转成短链接用户访问短链时能跳转到原地址请设计核心流程。回答这类题要有清晰的分层意识。我会按“存储—生成—跳转—扩展”四步组织答案。存储端用一张MySQL表就够了核心字段包括id、short_code、long_url、created_at。short_code是用户看到的那串六位左右长度的字符串long_url是原始地址。表上要建一个唯一索引在short_code上因为这个字段要参与查询。ID生成是真正的考点。很多人第一反应是直接用数据库自增主键然后转成62进制这种做法在单机场景没问题但面试官接下来一定会问“高并发下怎么办”。常见的扩展方案有几种一是用Redis的INCR命令生成ID性能好但要注意持久化二是用Snowflake雪花算法生成全局唯一ID不依赖数据库和Redis适合分布式场景三是预分配ID段每个应用服务器持有一段连续ID用完再去数据库取下一段这种方案在内部系统里很常用实现简单且性能稳定。回答完生成方案后还要主动补一个细节短链的跳转是301还是302我当时写了302临时重定向理由是便于统计点击量和变更目标地址如果使用301永久重定向浏览器会缓存目标地址后续想换链就麻烦了。在笔试中主动讲到这个细节通常能拿到额外加分。3.2 缓存、优化与容量评估让答案脱胎换骨的细节系统设计题要拿高分光有表和ID生成不够还需要体现出你对“读多写少”场景的理解。短URL系统的读请求量级远大于写请求所以一定要提到缓存层把热点短链的映射关系放到Redis里设置一个合理的过期时间比如24小时访问时先查缓存未命中再查数据库并回填缓存。至于缓存淘汰策略LRU是最自然的答案另外要提防缓存穿透——如果某个短链对应的长链不存在不能每次都打到数据库可以缓存空值或者用布隆过滤器拦截。容量评估也是笔试中容易被忽略的采分点。我习惯先估算QPS再推算存储量。假设系统同时服务内部和对外两个场景峰值QPS大约是2000其中99%是读请求那么每秒有1980次读来到缓存层。按Redis单实例十万级QPS来看一个实例完全够用再按每张MySQL表5万存储量估算一张表加索引不到2GB一年数据量大约在千万量级单表也能撑得住但最好还是按short_code做哈希分表或者直接预留分库分表的方案。这个计算过程只要在答案里体现出来就能和背模板的候选人明显拉开差距。4. 语言基础、操作系统与网络最容易丢分的暗坑4.1 C/Java考点对照内存、集合、并发选择题部分C和Java的考点高度固定我按两个方向整理了一下可以直接当自查清单用。C这边重点考三类内存管理、虚函数机制、STL容器底层。一个经典题是“类里面定义了虚构函数sizeof(类对象)是多少”答案是8字节64位系统下一个虚表指针而不是0。另一个高频题是vector扩容机制从容量不足时以1.5倍或2倍扩容、元素需要搬迁没有经历过扩容问题的同学很容易答错。智能指针也出现过题目会问shared_ptr可能有循环引用导致内存泄漏要用weak_ptr打破。Java这边考的是JVM内存区域、垃圾回收和集合类并发安全。HashMap在JDK1.7和1.8的区别几乎是必考项重点是1.8引入红黑树后链表长度超过8才转树同时头插法改成了尾插法防止并发扩容时出现循环链表。ConcurrentHashMap要讲到分段锁在1.7和CASsynchronized在1.8的区别。线程池的构造参数也考过需要答出corePoolSize、maximumPoolSize、workQueue和拒绝策略的关系。语言必须掌握的知识点常见丢分点C虚表指针、构造析构顺序、vector扩容、智能指针混淆编译器内存布局忽略默认构造和拷贝构造JavaJVM分区、GC算法、HashMap/ConcurrentHashMap、线程池记不清1.7与1.8差异线程状态转换顺序搞反4.2 操作系统与网络极简自查清单操作系统题在整个试卷里占比不算高但是选择题的固定板块。死锁的四个必要条件——互斥、持有并等待、不可剥夺、循环等待是高频题会问“破坏哪个条件可以预防死锁”。虚拟内存和页面置换也考过LRU算法需要会画访问序列的状态变化过程。进程和线程的区别属于送分题但问法会换个包装比如“多线程程序是否一定比多进程快”答案是不一定因为线程切换开销虽然小但线程间资源共享会带来锁竞争问题。计算机网络那边TCP三次握手和四次挥手是绝对重点。选择题喜欢考“SYN攻击利用的是哪一步”——这是三次握手第二次挥手之后服务器维护半连接队列等待ACK的过程。TIME_WAIT为什么存在也考过答案是保证最后一个ACK能到达对端同时让旧连接的延迟报文在网络中消逝等待时间是2MSL。HTTP部分会考状态码记住404、500、302、304的区别基本就够用304表示未修改可以配合浏览器缓存机制来理解。Cookie和Session的区别也是单选题常客核心是Cookie存在客户端、Session存在服务端。5. 笔试之后复盘与面试衔接的实操建议5.1 错题本不是抄题而是提炼解法模式笔试结束很多人对完答案就把题目忘了。我个人的习惯是当天晚上一定做复盘趁记忆还热乎。但复盘不是把错题抄一遍而是记录“这道题背后的解法模式”。比如一道“最长公共子序列”的题记下来的是“二维DP状态转移取决于当前字符是否相等”一道“链表找环”的题记下来的是“快慢指针相遇后从头再走一次”。这样积累几十道题之后你会发现算法题的类型其实很有限核心就是枚举、二分、DP、BFS/DFS、双指针、贪心这几种框架。我有个“一题三遍”的训练方法很推荐在实习笔试前使用。第一遍正常限时写题第二遍隔一天不看答案重新写第三遍把自己当成面试官对着代码讲解思路看能否讲得清楚。第三遍特别有用因为笔试后面大概率会有现场面试面试官会问“为什么用这个数据结构”“时间复杂度是多少”“能不能优化”你如果只在笔试层面会解题到了面试阶段很容易接不住追问。5.2 简历、项目与笔试成绩如何串联笔试只是第一道门槛之后简历筛选和面试环节会综合看你这个人。2015年那份卷子筛选出来的候选人进入面试后通常会被重点追问项目经历而且问得很细。我当时比较吃亏的是简历上写了一个“图片服务器”项目但准备不够充分面试官问“图片上传之后如何处理缩略图”“用什么存储引擎”“QPS能达到多少”我回答得比较支吾。这里给一个可复用的项目介绍框架先讲背景和要解决的问题再讲你的具体方案和选型理由最后讲量化结果。哪怕只是课程设计也可以强调数据量规模、并发数、查询耗时优化了多少。笔试成绩反映的是算法功底项目经历反映的是工程感两者要能互相印证。比如笔试里你系统设计题答了短URL方案简历里又恰好有类似的缓存设计面试官就会觉得你的经验是真实的。5.3 手撕代码练习中的几个习惯最后聊几个手撕代码的实操心得这些经验从笔试延伸到面试场景同样适用。第一变量命名要清晰不要写i、j、k到处飞至少让人一看就明白left、right是双指针边界cur、pre是链表前后节点。第二写完代码一定要自己用两个样例跑一遍一个正常场景、一个边界场景比如空数组、单节点链表、字符串长度为1。第三代码里尽量复用标准库不要自己造轮子比如std::reverse、Collections.sort不仅省时间而且不容易引入新bug。我在实际过程中还有一个习惯笔试前一周每天抽30分钟在白纸上手写一道树或DP类题目不用编辑器、不自动补全。这样做有两个好处一是锻炼代码在脑海里的编译通过能力避免笔试时因为缺分号、少括号这类低级错误扣分二是提前适应笔试卷面书写的节奏毕竟有些公司的笔试需要手写代码拍照上传字迹和排版也会影响评分。这个方法听起来笨但是真实有效至少我后面参加其他公司笔试时基本没再出现语法层面的低级失误。站在现在的角度看2015年那份笔试卷的很多题目在今天依然不过时。算法题考察的是思维底子系统设计题考察的是工程常识那几道选择题考察的是基本功扎不扎实。我到现在带新人的时候还会把短URL那道题拿出来作为入门案例用。笔试卷本身只是起点真正拉开差距的是你有没有从一次笔试里提炼出可复用的解题框架以及能不能在下一次笔试或面试里把这些框架用得更熟练。
返回列表