
秋招那会儿我把贝壳找房列进了自己的重点目标清单。原因很实际房产交易这个赛道足够大而算法在里面能落地的场景又非常具体——房源推荐、搜索排序、房价评估、VR带看背后的视觉处理每一块都直接跟业务挂钩。拿到“贝壳找房2023届校招算法卷1”这套题之后我完整做了一遍又陆陆续续跟几个进入面试环节的朋友对了对思路发现这套卷子的考察逻辑非常清晰不追求全网最偏最怪的题但基础知识的覆盖面极广数据结构、经典算法、机器学习理论、深度学习常识基本上都扫了一遍。这篇就当是我的复盘笔记把高频考点、真题推导、实战代码和踩坑经验一块儿整理出来希望对准备校招算法岗的同学有用。1. 校招算法卷的定位与考察逻辑1.1 为什么贝壳的算法卷和互联网大厂不一样刚拿到这套卷子的时候我其实是有点意外的。刷惯了字节、阿里那种偏“脑筋急转弯高难度动态规划”的风格贝壳这套卷子整体给人的感觉是“广而不偏”。后来我仔细想了想这跟公司的业务结构有很大关系。贝壳找房的业务核心是“房、客、经纪人”三边网络算法岗位方向拆得很开推荐算法做房源推荐和猜你喜欢搜索算法做房源检索和排序NLP方向做楼盘字典、智能问答和评论分析CV方向做户型图识别和VR看房还有专门做估价模型的团队用机器学习给二手房定价。这种情况下一套笔试算法卷不可能只偏向某一个方向它只能考察“所有算法岗都需要的公共底座”——数据结构、基础算法、机器学习原理、深度学习基本概念。所以你会发现这套卷子里很少出现那种“一个题目刁钻到需要奥数思维”的题反而大量考察基本功。比如排序、二分、KMP、贪心、动态规划以及LR推导、树模型、聚类、CNN结构这些。它的潜台词是我不要求你是某个细分方向的天才但你需要有扎实的计算机功底和足够的机器学习理论基础进组之后能快速上手业务。我当时还整理过一个对比表方便自己理解不同公司的出题风格公司类型出题风格侧重能力典型公司纯互联网大厂难题偏题多对逻辑思维要求极高竞赛思维、临场推演字节、阿里金融科技概率题和数学题占比高数学功底、建模能力各家量化、银行科技产业互联网平台广度优先基础扎实但不过度拔高综合能力、工程落地意识贝壳这类平台型公司当然这不绝对只是整体趋势。想进贝壳这类公司扎实吃透基础比盲目刷难题性价比高得多。1.2 算法卷的整体结构和时间分配我手里这份“贝壳找房2023届校招算法卷1”题型大致可以分成三类选择题/多选题主要覆盖数据结构、算法复杂度、机器学习基础概念大概20到30道。编程题2到4道以LeetCode中等题为主偶尔有一道简单题字符串、排序、动态规划是常客。简答/推导题部分批次会有比如手推LR梯度更新公式、解释Transformer的Attention机制。整场笔试时间通常在90到120分钟。我有两个很深的体会第一选择题不能恋战拿不准的先标记最后再回来抠。第二编程题一定要先把题目读懂、把边界条件想清楚再动手不要一上来就敲代码敲到一半发现思路错了更浪费。我见过不少同学在笔试时栽在时间分配上——前面选择题抠得太细后面编程题只剩20分钟结果白白丢了大分。我自己的策略是“先扫一遍所有题目编程题先花10分钟想思路选择题控制在1分钟一道以内给编程题留出至少60分钟”。这套策略在贝壳的卷子上实测是有效的。2. 高频考点知识体系从基础算法到机器学习2.1 数据结构与经典算法KMP、排序、堆、二分一个都不能漏贝壳算法卷对数据结构的考察非常稳定链表、栈、队列、二叉树、堆这些基本都会在选择题或编程题里出现。但要说哪个知识点最容易被单独拎出来考字符串算法里的KMP绝对算一个。我在复习KMP的时候一开始也是死记硬背结果换一个模式串就懵。后来我把它的核心逻辑彻底捋了一遍发现KMP其实就做了一件事当匹配失败时利用已经匹配的前缀信息让模式串指针不要回到开头而是跳到一个更合适的位置。这个“更合适的位置”就是next数组。next数组的定义在不同教材里略有差异。我们平时最常用的一种定义是next[i]表示模式串前i个字符组成的子串“最长相等真前后缀”的长度。举个很经典的例子模式串p abacaba它的next数组推导过程是这样的前1个字符a真前后缀为空next[0] 0。前2个字符ab前缀a、后缀b不相等next[1] 0。前3个字符aba前缀a和后缀a相等长度为1next[2] 1。前4个字符abac长度为1时前缀a、后缀c不等next[3] 0。前5个字符abaca前缀a和后缀a相等长度1next[4] 1。前6个字符abacab前缀ab和后缀ab相等长度2next[5] 2。前7个字符abacaba前缀aba和后缀aba相等长度3next[6] 3。所以p abacaba的next数组最长相等前后缀版本是[0, 0, 1, 0, 1, 2, 3]。但我必须提醒一句网上很多教程用的next定义是“失配时模式串指针跳转的位置”也就是把上面的结果整体右移一位初始值置为-1。两种定义写出来的代码长不一样面试时一定要先跟面试官确认或者直接在代码注释里写明白自己的定义。我自己就在一次模拟面试里因为默认用了-1版本和面试官的0版本对不上差点翻车。KMP匹配的完整代码我习惯写成下面这样def build_next(p): m len(p) nxt [0] * m for i in range(1, m): j nxt[i - 1] while j 0 and p[i] ! p[j]: j nxt[j - 1] if p[i] p[j]: j 1 nxt[i] j return nxt def kmp_search(text, p): n, m len(text), len(p) if m 0: return 0 nxt build_next(p) j 0 for i in range(n): while j 0 and text[i] ! p[j]: j nxt[j - 1] if text[i] p[j]: j 1 if j m: return i - m 1 return -1这套代码我秋招期间默写了很多遍核心就一个字稳。排序算法也是贝壳笔试的常客。冒泡、快排、归并、堆排序这几个必须能手写。特别是快速排序考的概率极高而且经常会被问“最坏情况下时间复杂度是多少”“如何避免最坏情况”。我当时特意整理了一个对比表格排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n^2)O(n^2)O(1)稳定快速排序O(n log n)O(n^2)O(log n)不稳定归并排序O(n log n)O(n log n)O(n)稳定堆排序O(n log n)O(n log n)O(1)不稳定快速排序的优化方式也要清楚比如三数取中、小数组切换插入排序、递归改迭代。选择题里经常考这些细节编程题里如果能体现出来也是加分项。二分查找同样重要但它考的不是“你会不会写”而是“边界会不会写错”。left right还是left rightmid (left right) // 2还是mid left (right - left) // 2这些细节笔试里一错就是WA。我的经验是统一记住一套模板别每次现想考试时不动脑子直接默写最靠谱。2.2 动态规划与贪心算法考察的不是公式是思维贝壳的编程题几乎每年都会涉及动态规划。爬楼梯、最长公共子序列、0-1背包、编辑距离这些经典题目我都刷过而且都总结成了固定的解题套路。动态规划的核心就三步定义状态、写转移方程、初始化边界。拿0-1背包举例。有n个物品每个物品有重量w[i]和价值v[i]背包容量为C求能装下的最大价值。状态定义为dp[i][j]表示前i个物品装入容量为j的背包能获得的最大价值转移方程为dp[i][j] max(dp[i-1][j], dp[i-1][j-w[i]] v[i])初始化dp[0][j] 0。这个题还可以压缩成一维数组内层j倒序遍历很多笔试题都会在这一步设坎。我当时在一道改编题里就是因为忘了倒序WA了两次才反应过来。贪心算法也是一个高频考点但贝壳考察贪心的方式一般不会太偏更多是让你判断“这个场景能不能用贪心”以及解释“贪心和动态规划的区别”。我当时的理解是贪心每一步都做当前最优选择不管后续动态规划则会把所有子问题的结果都算出来再综合决策。能同时用两种思路解的题很典型比如“和最大的连续子数组”可以用贪心也可以用DP但“找零钱最少硬币数”如果硬币面额不规则贪心就失效了只能用DP。其实算法题考察的本质就是思维模式。你平时解题时有没有形成从“暴搜-优化-DP/贪心”的思维链路比背多少道题重要得多。贝壳这类公司的算法卷恰好就喜欢考这种“你能不能把基础思维模型灵活应用”的能力。2.3 机器学习与深度学习基础不止是背公式贝壳算法卷的选择题和简答题里机器学习基础的占比不低。我印象比较深的知识点有这些逻辑回归损失函数为什么用交叉熵而不用均方误差梯度下降的推导。决策树与集成学习ID3、C4.5、CART的区别随机森林和GBDT、XGBoost的基本思想。聚类算法K-means的流程、K值怎么选、K-means的缺点。降维PCA的原理为什么要中心化。过拟合正则化L1和L2的区别L1为什么能产生稀疏解。逻辑回归的推导几乎是必考。它本身是分类模型但名字里有“回归”因为它的决策边界是线性的。核心公式是p 1 / (1 exp(-z))其中z w^T x b。训练时用梯度下降最小化交叉熵损失。我建议每个准备算法岗的人都亲手推一遍梯度更新公式笔试时如果真的考推导拿着笔能直接写出来印象分完全不一样。深度学习方面反向传播、softmax和交叉熵的组合、CNN卷积核的尺寸计算、RNN的梯度消失、Attention机制和Transformer都是高频选择题考点。特别要注意是“KL散度”这个概念当时考到了它和交叉熵的关系我一开始还反应了十几秒。KL散度衡量两个分布之间的差异公式是KL(P||Q) sum(P(x) log(P(x)/Q(x)))而交叉熵等于sum(P(x) log(1/Q(x)))两者相差一个熵项。如果P是真实分布且固定最小化交叉熵等价于最小化KL散度。这个概念在VAE的ELBO推导里也会遇到属于那种看着冷门但真考出来就很有区分度的点。另外还有一个小趋势启发式算法在简答题中也偶尔出现。比如粒子群算法和模拟退火算法的基本原理。我当时复习的时候觉得这些算法在互联网公司笔试里出现概率很低但贝壳因为业务里有估价模型、路径规划这类场景反而有可能涉及。粒子群的核心就是一群粒子在解空间里飞行每个粒子根据自身历史最优和全局最优更新速度与位置模拟退火则是以一定概率接受更差的解避免陷入局部最优。这两个算法不需要深入代码但至少要知道它们解决什么问题、与梯度下降思路有什么区别。3. 实战真题解析代码与推导过程3.1 模式串 abacaba 的 next 数组两种定义都要掌握热词里有一条“在kmp算法中对于模式串pabacaba其next数组(next[i]定义为...”说明这个题目在搜索热度上非常高。我前面已经推导了“最长相等前后缀”版本的next数组答案是[0, 0, 1, 0, 1, 2, 3]这里再做一个补充。如果面试官用的next定义是“失配时j应该跳转到的位置”也就是常见的next[j]表示当模式串中第j个字符匹配失败时j应该回退到哪个位置。这种定义下数组值等于最长相等前后缀长度整体右移一位然后把next[0]置为-1。按这个规则p abacaba的next数组就是[-1, 0, 0, 1, 0, 1, 2]。我当时为了彻底搞清楚这两种定义还专门在草稿纸上画了匹配过程的示意图。KMP匹配时假设主串是abacabacababa当模式串匹配到最后一个字符失败时有了next数组我们就能直接把j跳到next[j]而不是回到0重新匹配。这个跳转逻辑是整个KMP算法的灵魂。笔试里出现这类题大概率不是让你写完整KMP而是考你是否理解next数组的含义。所以备考时不要只背代码一定要能自己手推模式串的next数组并且能解释清楚next数组每个值是“怎么来的、有什么含义”。3.2 手写快速排序边界条件与性能优化缺一不可快排在编程题里属于“看起来简单写对不容易”的那种。很多同学都能说出快排的核心思想——选基准、分区、递归但一写代码就出错。我见过最多的错误有两个一是分区时左右指针越界二是递归结束条件写错导致无限递归。我自己的快排模板是双指针分区版def quick_sort(arr, left, right): if left right: return pivot arr[left] i, j left, right while i j: while i j and arr[j] pivot: j - 1 arr[i] arr[j] while i j and arr[i] pivot: i 1 arr[j] arr[i] arr[i] pivot quick_sort(arr, left, i - 1) quick_sort(arr, i 1, right)这个版本把基准值先存到pivot变量里利用双指针不断覆盖空位最后把pivot放回正确位置。好处是代码短、不容易出错。但要注意如果基准始终选最左边的元素数组本身有序时就会退化成O(n^2)。所以笔试里如果要求优化我一般会加一个三数取中的步骤def get_pivot(arr, left, right): mid (left right) // 2 if arr[left] arr[mid]: arr[left], arr[mid] arr[mid], arr[left] if arr[left] arr[right]: arr[left], arr[right] arr[right], arr[left] if arr[mid] arr[right]: arr[mid], arr[right] arr[right], arr[mid] arr[left], arr[mid] arr[mid], arr[left] return arr[left]取三个数中间值作为基准能大幅降低最坏情况出现的概率。面试时如果你能主动写出三数取中面试官会觉得你“有工程意识”这不只是加分项很多时候是把一个B评价拉成A评价的关键。3.3 从爬楼梯到背包问题动态规划的流水线解法贝壳的编程题里有一类很典型的动态规划题难度介于LeetCode中等偏下。我自己准备时把DP题目按“选或不选”“走到当前位置”“区间划分”等模型做了分类考试时看题就能判断属于哪一类思路来得快很多。以爬楼梯为例每次可以爬1阶或2阶问爬到n阶有多少种不同方法。状态定义dp[i]表示爬到第i阶的方法数转移方程dp[i] dp[i-1] dp[i-2]边界dp[0]1, dp[1]1。代码很简单def climb_stairs(n): if n 1: return 1 dp [0] * (n 1) dp[0], dp[1] 1, 1 for i in range(2, n 1): dp[i] dp[i - 1] dp[i - 2] return dp[n]这道题的进阶版是“最小花费爬楼梯”核心思路一模一样只是转移方程变成了dp[i] min(dp[i-1] cost[i-1], dp[i-2] cost[i-2])。这类题在笔试里考的其实就是“你有没有见过这个模型”。0-1背包则更综合一点。我之前在贝壳的一道编程模拟题里碰到过它的变形每个房源有一个“价值分”和一个“带看成本”预算有限怎么选一组房源让总价值分最大。这就是典型的0-1背包。我当时直接把一维DP的模板套上去十行代码解决。def knapsack(weights, values, capacity): dp [0] * (capacity 1) for i in range(len(weights)): for j in range(capacity, weights[i] - 1, -1): dp[j] max(dp[j], dp[j - weights[i]] values[i]) return dp[capacity]这里一定要记住的是内层循环从大到小遍历。如果正序同一个物品可能被重复选择那就变成完全背包了。这个细节笔试里年年有人踩坑。3.4 手写逻辑回归梯度下降与softmax反向传播有些批次的贝壳算法卷会有简答题要求手写逻辑回归的梯度更新公式。我当时是这么准备的先写前向传播再写损失然后求导。逻辑回归的前向传播import numpy as np def sigmoid(z): return 1 / (1 np.exp(-z)) def predict(X, w, b): z np.dot(X, w) b return sigmoid(z)交叉熵损失def loss(y_true, y_pred): eps 1e-12 y_pred np.clip(y_pred, eps, 1 - eps) return -np.mean(y_true * np.log(y_pred) (1 - y_true) * np.log(1 - y_pred))梯度更新def gradient_descent(X, y_true, w, b, lr0.01, epochs100): m len(y_true) for _ in range(epochs): y_pred predict(X, w, b) dw np.dot(X.T, y_pred - y_true) / m db np.mean(y_pred - y_true) w - lr * dw b - lr * db推导的关键在于交叉熵损失对sigmoid输出求导后会得到非常简洁的形式(y_pred - y_true) / m。这也是为什么逻辑回归通常用交叉熵而不是均方误差——交叉熵加sigmoid求导形式简洁均方误差加sigmoid会有sigmoid导数的饱和项梯度容易消失。softmax和交叉熵的组合也很常考。假设模型输出为z [z1, z2, ..., zk]softmax概率为p_i exp(z_i) / sum_j exp(z_j)交叉熵损失为L -log(p_y)y是真实类别。求导结果非常漂亮∂L/∂z_i p_i - 1当i y否则∂L/∂z_i p_i。写成代码就是def softmax_cross_entropy_grad(z, y): exp_z np.exp(z - np.max(z)) p exp_z / np.sum(exp_z) grad p.copy() grad[y] - 1 return gradz - np.max(z)是为了数值稳定性这个细节在实现里几乎必写。面试官问“为什么要减max”你要能答出“防止exp溢出”这一层他基本就会点头。4. 面试复盘与避坑指南4.1 刷题时间规划按阶段拆解而不是盲目刷量我见过很多同学准备校招算法题一上来就刷LeetCode热题100刷到一半觉得太难放弃了。我的建议是分三个阶段走第一阶段基础期数据结构全过一遍链表、栈、队列、二叉树、堆、图的基本操作配合LeetCode简单题巩固。第二阶段核心期按专题刷题二分、排序、双指针、滑动窗口、DFS/BFS、动态规划、贪心每个专题至少刷15道达到“看到题就能归到某个专题”的熟练度。第三阶段冲刺期刷高频题和模考真题每周至少完整做一套笔试卷子模拟限时环境。贝壳这个级别的公司第二阶段做扎实基本就够了。其实笔试挂人更多是因为“明明会做但写错了”或者“时间不够”而不是“遇到完全不会的题”。所以平时练习一定要限时尤其是编程题一道题最多40分钟超时就看题解并总结原因。4.2 笔试现场最容易丢分的三类原因丢分原因一不先确认输入范围。很多题会给出数据范围比如n最大10^5这就说明O(n^2)的算法会超时应该往O(n log n)或O(n)方向想。如果忽略数据范围写出来的算法复杂度不对样例过了但后台大数据全挂。丢分原因二边界条件处理缺失。输入为空、只有一个元素、目标不存在、负数、溢出这些边界几乎每道题都值得检查一遍。我养成了一个习惯写完代码先不急着提交自己补几个边界用例在本地跑一下跑通了再交。丢分原因三代码风格混乱。变量名用a、b、c注释不写缩进乱都会让面试官在看代码的时候产生不必要的负面印象。笔试卷子有时候会进入面试环节被面试官调出来看一份干净的代码绝对能提升印象分。4.3 代码调试技巧边界值、异常输入与复杂度分析我在现场笔试时很少依赖编译器调试因为平台环境不一定给你断点调试。更实用的方法是“人工走查”先按正常路径走一遍再用最小例子走一遍最后用一个极端例子走一遍。比如你写了一个找数组中位数的函数可以手动测奇数长度数组、偶数长度数组、长度为1的数组、空数组。每个用例走一遍逻辑基本能把80%的bug消灭掉。另一个实用技巧是输出中间值。在关键循环里打印几个变量快速定位逻辑错误在哪里。笔试平台一般都允许print只要提交前把调试输出删掉就行。我见过有人交了带print的代码样例过了但被判错其实就是忘了删输出。复杂度分析也要在答题最后写清楚。编程题如果题目要求“请给出时间复杂度和空间复杂度”千万不要漏。就算题目没要求在代码注释里写一下复杂度也是一个加分细节。4.4 面试官追问策略做对题只是及格线贝壳的面试通常在笔试通过后进行流程一般是两到三轮技术面加一轮HR面。技术面的第一轮大概率会问笔试中的编程题让你讲讲思路、看看有没有更好的解法。这时候如果你能说出“我当时的解法复杂度是多少还可以用哪种方法优化”会比只写对代码高出几个档次。举个我自己的例子一道二维矩阵搜索题我先给了暴力遍历的O(m*n)解法然后补充说有序矩阵可以用“从右上角开始搜索”把复杂度降到O(mn)。面试官明显对这个回答很认可后续环节聊得也比较顺畅。另外面试中如果问到机器学习项目一定要把“业务问题—数据—特征—模型—评估—上线”这段闭环讲清楚。贝壳是产业互联网公司非常看重你对业务场景的理解。算法卷只验证“你会不会”面试验证的是“你能不能在实际问题上把它用起来”。还有一个容易被忽略的点准备两三个反问问题。面试结束前问一下“团队目前主要做什么方向”“新人进来后如何上手业务”会让面试官觉得你是真的对这个岗位有兴趣而不是海投瞎碰。最后再分享一点个人经验把“贝壳找房2023届校招算法卷1”完整复盘下来我最大的感受是这套题不考天才考的是习惯。你是不是养成了边界检查的习惯是不是掌握了经典算法的模板能不能把机器学习的原理推导清楚这些都直接决定了笔试成绩。我自己在准备过程中踩过不少坑最典型的是前期只刷难题不巩固基础导致选择题里一些基础概念反而拿不准。后来我把重心八成放到基础、两成放到拔高效果明显好很多。还有一点就是代码一定要动手写不要眼高手低——看题解和默写代码是完全两回事只有真正落到纸面上你才会发现逻辑漏洞在哪。如果你正在准备贝壳或者其他产业互联网公司的算法岗我的建议很直接算法基础按专题过机器学习原理亲手推一遍公式编程题保持手感面试前把业务场景了解清楚。做到这几点这套流程你基本就能稳住了。