☰
贝壳找房2023届校招算法卷复盘:KMP、快排与动态规划全解析
2026/10/17 0:32:20 网站建设 项目流程

秋招那会儿,我把贝壳找房列进了自己的重点目标清单。原因很实际:房产交易这个赛道足够大,而算法在里面能落地的场景又非常具体——房源推荐、搜索排序、房价评估、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相等,长度为1,next[2] = 1。
  • 前4个字符"abac",长度为1时前缀a、后缀c不等,next[3] = 0。
  • 前5个字符"abaca",前缀a和后缀a相等,长度1,next[4] = 1。
  • 前6个字符"abacab",前缀ab和后缀ab相等,长度2,next[5] = 2。
  • 前7个字符"abacaba",前缀aba和后缀aba相等,长度3,next[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 <= right,mid = (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算法中,对于模式串p='abacaba',其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, lr=0.01, epochs=100): 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 grad

z - 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(m+n)。面试官明显对这个回答很认可,后续环节聊得也比较顺畅。

另外,面试中如果问到机器学习项目,一定要把“业务问题—数据—特征—模型—评估—上线”这段闭环讲清楚。贝壳是产业互联网公司,非常看重你对业务场景的理解。算法卷只验证“你会不会”,面试验证的是“你能不能在实际问题上把它用起来”。

还有一个容易被忽略的点:准备两三个反问问题。面试结束前问一下“团队目前主要做什么方向”“新人进来后如何上手业务”,会让面试官觉得你是真的对这个岗位有兴趣,而不是海投瞎碰。

最后再分享一点个人经验

把“贝壳找房2023届校招算法卷1”完整复盘下来,我最大的感受是:这套题不考天才,考的是习惯。你是不是养成了边界检查的习惯,是不是掌握了经典算法的模板,能不能把机器学习的原理推导清楚,这些都直接决定了笔试成绩。

我自己在准备过程中踩过不少坑,最典型的是前期只刷难题不巩固基础,导致选择题里一些基础概念反而拿不准。后来我把重心八成放到基础、两成放到拔高,效果明显好很多。还有一点就是代码一定要动手写,不要眼高手低——看题解和默写代码是完全两回事,只有真正落到纸面上,你才会发现逻辑漏洞在哪。

如果你正在准备贝壳或者其他产业互联网公司的算法岗,我的建议很直接:算法基础按专题过,机器学习原理亲手推一遍公式,编程题保持手感,面试前把业务场景了解清楚。做到这几点,这套流程你基本就能稳住了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询