【算法】0_算法工程师常见算法题
【算法】0_算法工程师常见算法题
文章目录
- 0_算法工程师常见算法题
- 一、基础数据结构相关题型(高频必掌握)
- 1. 数组与字符串
- 2. 链表
- 3. 栈与队列
- 4. 哈希表
- 5. 树与图
- 二、经典算法思想题型(解题核心方法)
- 1. 贪心算法
- 2. 动态规划(DP)
- 3. 分治算法
- 4. 回溯算法
- 5. 双指针与滑动窗口
- 三、工程实践与优化题型(高阶考察)
- 1. 海量数据处理
- 2. 排序与查找进阶
- 3. 图论工程应用
- 四、面试核心考察点(不止于“做对题”)
- 总结
0_算法工程师常见算法题
算法工程师面试中,算法题型的考察核心是**“基础扎实性+问题建模能力+工程优化思维”,既覆盖经典数据结构与算法思想,也结合实际业务场景(如海量数据、推荐搜索等)。以下按「核心分类」拆解常见题型,包含考察重点、典型例题、解题关键**及对应LeetCode链接,帮助系统梳理。
一、基础数据结构相关题型(高频必掌握)
所有算法都依赖数据结构的合理选择,这部分是面试“保底题”,初级/中级面试占比50%以上,重点考察对结构特性的理解(如数组随机访问、链表动态增删、哈希表O(1)查询)及边界处理能力。
1. 数组与字符串
- 考察重点:随机访问特性、边界条件(空数组、重复元素、正负值)、时间复杂度优化(避免O(n²)暴力)。
- 典型例题(附LeetCode链接):
- 两数之和:LeetCode 1,基础哈希表应用,优化暴力解;
- 最长无重复子串:LeetCode 3,双指针/滑动窗口,用哈希表记录字符位置;
- 最长回文子串:LeetCode 5,中心扩展法/动态规划,理解回文的对称性;
- 接雨水:LeetCode 42,双指针/单调栈,建模“每个位置的存水量=min(左max,右max)-当前高度”。
- 解题关键:优先考虑“双指针”“滑动窗口”减少嵌套循环,用哈希表/数组记录中间状态(如字符位置、前缀和)。
2. 链表
- 考察重点:链表的非线性访问特性(只能从头遍历)、指针操作(避免断链)、特殊场景(环、相交、反转)。
- 典型例题(附LeetCode链接):
- 反转链表:LeetCode 206,迭代(双指针)/递归,理解“prev-curr-next”的指针转移;
- 环形链表(判断环存在):LeetCode 141;环形链表II(找环入口):LeetCode 142,均用快慢指针(Floyd算法);
- 合并两个有序链表:LeetCode 21,迭代/递归,虚拟头节点简化边界;
- 链表中倒数第k个节点:LeetCode 剑指Offer 22,快慢指针(快指针先走k步)。
- 解题关键:复杂链表题(如两两交换、重排链表)可先画示意图,用“虚拟头节点”处理头节点特殊情况,递归解法需明确终止条件。
3. 栈与队列
- 考察重点:栈“后进先出”(LIFO)、队列“先进先出”(FIFO)的特性,及衍生结构(单调栈、优先队列)。
- 典型例题(附LeetCode链接):
- 有效的括号:LeetCode 20,栈匹配(左括号入栈,右括号出栈校验);
- 最小栈:LeetCode 155,辅助栈记录当前最小值,空间换时间;
- 滑动窗口最大值:LeetCode 239,单调队列(维护窗口内递减序列,队首为最大值);
- 用栈实现队列:LeetCode 232;用队列实现栈:LeetCode 225,理解两种结构的特性转换。
- 解题关键:单调栈/单调队列是核心工具(如“下一个更大元素”问题必用单调栈),需记住“单调栈适合找左右近邻的极值”,“单调队列适合滑动窗口的极值维护”。
4. 哈希表
- 考察重点:哈希表的“键值对”映射、哈希冲突处理(链地址法/开放地址法)、工程优化(负载因子、哈希函数设计)。
- 典型例题(附LeetCode链接):
- 两数之和:LeetCode 1,哈希表存“值→索引”,O(n)时间;
- 最长连续序列:LeetCode 128,哈希表存所有元素,遍历每个序列的起点(避免重复计算);
- 字母异位词分组:LeetCode 49,哈希表键为“排序后的字符串”,值为异位词列表;
- LRU缓存:LeetCode 146,哈希表+双向链表,实现O(1)存取和删除最近最少使用节点。
- 解题关键:哈希表的核心是“用空间换时间”,需明确“键”的设计(如异位词用排序后字符串、坐标问题用“x,y”拼接字符串),LRU是工程高频题,需掌握双向链表的插入/删除细节。
5. 树与图
- 考察重点:树的层次/深度遍历、二叉搜索树(BST)的特性、图的遍历(BFS/DFS)、最短路径等。
- 典型例题(附LeetCode链接):
- 树相关:
- 二叉树的层序遍历:LeetCode 102,BFS(队列),按层处理节点;
- 二叉树的最大深度:LeetCode 104,DFS(递归)/BFS(层计数);
- 验证二叉搜索树:LeetCode 98,中序遍历是否递增,或递归时传递“上下界”;
- 二叉树的最近公共祖先:LeetCode 236,递归找左右子树是否包含目标节点,分情况判断。
- 图相关:
- 岛屿数量:LeetCode 200,DFS/BFS遍历网格,标记已访问节点;
- 课程表:LeetCode 207,拓扑排序(BFS入度表/DFS环检测),解决“依赖关系”问题;
- 网络延迟时间(最短路径):LeetCode 743,Dijkstra算法(有权图,无负权),Floyd算法(多源最短路径,适合小规模图)。
- 树相关:
- 解题关键:树的遍历是基础(递归/迭代实现前中后序),BST的核心是“中序遍历有序”;图的问题需先构建邻接表(adjacency list),拓扑排序和最短路径是工程中“任务调度”“路线规划”的基础。
二、经典算法思想题型(解题核心方法)
算法思想是“解题的套路”,面试中需用这些思想建模问题,重点考察“是否能识别问题类型并选择合适方法”,中级/高级面试占比40%。
1. 贪心算法
- 考察重点:贪心的适用条件(最优子结构+无后效性),即“每一步选局部最优,最终得到全局最优”,需能证明贪心策略的正确性。
- 典型例题(附LeetCode链接):
- 活动选择问题(类似):LeetCode 435,选择最多不重叠的区间,策略“选最早结束的”;
- 分发饼干:LeetCode 455,小饼干先满足小胃口,排序后双指针;
- 跳跃游戏:LeetCode 55,维护“当前能到达的最远距离”,遍历更新;
- 哈夫曼编码(类似,最小费用合并):LeetCode 1167,每次选两个最小权重节点合并(优先队列实现)。
- 解题关键:贪心策略需“可证明”(如反证法),若无法证明则可能需用动态规划;常见贪心场景:区间问题(排序后选端点)、资源分配(按优先级匹配)。
2. 动态规划(DP)
- 考察重点:DP是面试难点,核心考察“状态定义”“转移方程”“边界条件”,解决“重叠子问题”和“最优子结构”问题。
- 典型例题(附LeetCode链接):
- 基础DP:斐波那契数列:LeetCode 509;爬楼梯:LeetCode 70,理解“状态复用”;
- 二维DP:最长公共子序列(LCS):LeetCode 1143,状态dp[i][j] = 前i个字符与前j个字符的LCS长度;
- 背包问题:
- 01背包(分割等和子集):LeetCode 416,选/不选第i个物品,dp[j] = max(dp[j], dp[j-w[i]] + v[i])(逆序遍历容量);
- 完全背包(零钱兑换II):LeetCode 518,物品可重复选,正序遍历容量;
- 区间DP:最长回文子序列:LeetCode 516,状态dp[i][j] = 区间[i,j]的最长回文子序列长度;
- 状态压缩DP:旅行商问题(TSP,类似):LeetCode 943,用二进制表示已访问节点,状态dp[mask][u] = 访问mask城市后到u的最短路径。
- 解题关键:DP的第一步是“定义状态”(需明确dp[i]或dp[i][j]代表什么),第二步推导转移方程(从子问题到当前问题),第三步处理边界(如dp[0]、dp[i][0]);复杂问题可先画“DP表”(如LCS的二维表)辅助理解。
3. 分治算法
- 考察重点:分治的核心是“分而治之”(将问题拆分为子问题→解决子问题→合并结果),常与递归结合,考察对问题拆分逻辑的理解。
- 典型例题(附LeetCode链接):
- 归并排序:LeetCode 912,拆分为左右子数组→分别排序→合并两个有序数组;
- 快速排序(参考排序题):LeetCode 912,选pivot→分区(小于pivot放左,大于放右)→递归排序左右;
- 寻找两个正序数组的中位数:LeetCode 4,分治拆分两个数组,每次排除一半不可能的区间;
- 多数元素:LeetCode 169,分治找左右子数组的多数元素,再统计全局多数。
- 解题关键:分治的难点是“合并子问题结果”(如归并排序的合并步骤),需注意拆分后的子问题要“独立且规模相同”(便于递归);快排的pivot选择(如随机pivot)可避免最坏O(n²)时间。
4. 回溯算法
- 考察重点:回溯是“暴力搜索的优化”,通过“试错+剪枝”遍历所有可能解,考察“状态回溯”和“剪枝条件设计”(减少无效搜索)。
- 典型例题(附LeetCode链接):
- 全排列:LeetCode 46,标记已选元素,递归选未选元素,回溯时撤销标记;
- 组合总和:LeetCode 39,排序后按顺序选元素,避免重复组合,剪枝“当前元素大于目标时停止”;
- N皇后:LeetCode 51,用三个集合标记列、正对角线、反对角线,剪枝无效位置;
- 子集:LeetCode 78,按元素顺序递归,每个元素选/不选,无需回溯(因子集不要求顺序)。
- 解题关键:回溯的模板是“递归函数(当前状态、已选结果)→终止条件→遍历可选选项→选当前选项→递归→回溯(撤销选项)”;剪枝是优化核心(如排序后跳过重复元素、提前终止无效路径)。
5. 双指针与滑动窗口
- 考察重点:针对“线性数据(数组/字符串)”的高效遍历方法,减少嵌套循环,将时间复杂度从O(n²)降至O(n)。
- 典型例题(附LeetCode链接):
- 双指针(相向):三数之和:LeetCode 15,排序后固定一个数,左右指针找另外两个数,剪枝重复;
- 双指针(同向):移除元素:LeetCode 27,慢指针记录有效位置,快指针遍历数组;
- 滑动窗口:
- 最小覆盖子串:LeetCode 76,窗口扩张找包含所有目标字符,收缩优化最小长度;
- 长度最小的子数组:LeetCode 209,窗口扩张到满足条件,收缩找最小长度。
- 解题关键:双指针需明确“指针移动方向”(相向/同向)和“移动条件”(如三数之和中左指针右移当和太小,右指针左移当和太大);滑动窗口需区分“固定窗口”和“可变窗口”,用哈希表记录窗口内元素状态(如字符计数)。
三、工程实践与优化题型(高阶考察)
算法工程师需将算法落地到工程,这部分考察“实际场景的问题转化”和“性能优化”,高级面试/资深岗占比30%以上。
1. 海量数据处理
- 考察重点:面对“数据量超过内存”(如10亿个数、1TB日志),如何用“分治、哈希分片、概率数据结构”解决,核心是“减少单次处理的数据量”。
- 典型例题:
- 海量数据找Top K:分治(将数据分片到多台机器/文件,每片找Top K,再合并Top K)+ 小顶堆(维护当前Top K,O(n log K)),参考题:LeetCode 215(单机Top K基础版);
- 海量数据去重:布隆过滤器(Bloom Filter,概率去重,无假阴性,有一定假阳性),参考实现思路题:LeetCode 187(用哈希/位运算实现重复序列检测,可类比布隆过滤器的去重思想);或哈希分片后单机去重(类似分布式场景的简化);
- 海量日志统计IP出现次数:哈希分片(按IP哈希值分到不同文件)→ 单机用哈希表统计→ 合并结果,参考单机统计题:LeetCode 451(字符频次统计,核心是“计数+排序”,与IP统计逻辑一致);
- 外部排序:分治(将大文件拆分为小有序文件)→ 多路归并(合并小有序文件为大有序文件),参考归并排序的多路扩展:LeetCode 23(合并k个有序链表,可类比多路归并的核心逻辑)。
- 解题关键:记住“海量数据三板斧”:分治(拆数据)、哈希(划分子集)、概率结构(降空间),需理解布隆过滤器的原理(m位bit数组+k个哈希函数)和误差率计算(公式:(1 - e(-kn/m))k,k为哈希函数个数,n为数据量,m为bit数组长度)。
2. 排序与查找进阶
- 考察重点:除了基础排序(快排、归并),还考察排序的工程优化(如TimSort、IntroSort)、查找的变种(如模糊匹配、范围查询)。
- 典型例题(附LeetCode链接):
- 排序优化:快排的pivot选择(随机pivot、三数取中)、处理重复元素(三路快排,分为小于/等于/大于pivot的区间),参考题:LeetCode 75(荷兰国旗问题,本质是三路快排的分区逻辑);
- 二分查找变种:
- 搜索插入位置(找第一个大于等于目标的位置):LeetCode 35;
- 寻找旋转排序数组中的最小值:LeetCode 153,区分“左半有序”和“右半有序”,排除非最小值区间;
- 寻找两个正序数组的中位数(二分+分治):LeetCode 4(前文已提及,属于二分查找的高阶应用);
- 字符串匹配:KMP算法(预处理前缀函数,避免主串回溯),参考题:LeetCode 28(实现strStr(),需用KMP优化暴力匹配);BM算法(从后往前匹配,坏字符/好后缀规则,工程中更高效,无直接LeetCode题,但需理解核心优化逻辑)。
- 解题关键:二分查找的核心是“边界条件”(需明确循环终止条件是
left <= right
还是left < right
,以及更新left
和right
时是否加1),需区分“找存在的元素”“找左边界”“找右边界”三种场景;字符串匹配中,KMP的前缀函数是关键(prefix[i]
表示字符串s[0..i]
的最长相等前后缀长度,需掌握前缀数组的推导过程)。
3. 图论工程应用
- 考察重点:图论在实际业务中的落地,如社交网络、路径规划、推荐系统,考察“图的构建”和“算法选型”(如稀疏图用邻接表,稠密图用邻接矩阵;无负权图用Dijkstra,有负权图用Bellman-Ford或SPFA)。
- 典型例题(附LeetCode链接):
- 社交网络好友推荐:基于“共同好友”(图中节点的交集)或“最短路径”(朋友的朋友),参考题:LeetCode 851(图的多源最短路径变种,可类比“好友关系的层级推荐”);
- 地图路径规划:
- Dijkstra算法(最短路径,无负权):LeetCode 743(计算信号从起点到所有节点的最短时间,即单源最短路径问题);
- A*算法(加入启发函数,优化搜索方向,工程常用):无直接LeetCode题,核心是“估价函数
f(n) = g(n) + h(n)
”(g(n)
是起点到n的实际距离,h(n)
是n到终点的估计距离,需满足h(n) ≤ 实际距离
以保证最优解);
- 强连通分量(SCC):Tarjan算法(找图中相互可达的子图),应用于“社区发现”“依赖环检测”,参考题:LeetCode 1192(找桥问题,Tarjan算法的典型应用,可类比SCC的遍历逻辑);
- 拓扑排序工程应用:LeetCode 210(课程表II,输出拓扑排序结果,对应“任务调度依赖”场景)。
- 解题关键:工程中构建图需先判断“数据规模”(如社交网络用户数亿,用邻接表存储;地图小区域道路,可用邻接矩阵);算法选型需权衡“时间复杂度”和“实现难度”(如Dijkstra用优先队列优化后时间复杂度O(M log N),适合大规模稀疏图;Floyd算法O(N³),仅适合小规模图)。
四、面试核心考察点(不止于“做对题”)
算法题面试不只是“写出正确代码”,还需满足以下要求,否则易丢分:
- 复杂度分析:每道题需明确说明“时间复杂度”和“空间复杂度”,并解释原因(如“两数之和用哈希表,时间O(n)是因为遍历一次数组,空间O(n)是因为哈希表最多存n个元素”;“归并排序时间O(n log n)是因为拆分log n层,每层合并O(n),空间O(n)是因为需要临时数组存合并结果”);
- 边界条件处理:需覆盖“空输入(如空数组、空链表、空字符串)、极值(如数组长度为1、数值超出int范围)、重复元素(如三数之和中的重复三元组、链表中的重复节点)”等场景,避免代码运行崩溃或输出错误;
- 代码规范性:变量命名清晰(如用
left
/right
表示双指针,prev
/curr
表示链表节点,避免i
/j
/a
/b
等模糊命名)、注释简洁(说明核心逻辑,如“此处用单调栈维护窗口内递减序列,队首为当前窗口最大值”)、避免冗余代码(如用函数封装重复的“合并两个有序数组”逻辑); - 原理理解与优化:面试官常追问“为什么用这个方法”(如“为什么用单调栈解决接雨水,而不用暴力遍历?”→“因为单调栈能一次遍历找到每个位置左右的最高柱,时间O(n),比暴力O(n²)更高效”)、“有没有更优解”(如“反转链表用迭代比递归更优,因为递归会占用O(n)栈空间,迭代仅需O(1)空间”)、“工程中如何优化”(如“快排处理大量重复元素时,用三路快排避免分区不平衡,降低时间复杂度”)。
总结
算法工程师面试的算法题型可归纳为“基础数据结构→经典算法思想→工程实践”的递进关系,建议备考优先级:
- 先掌握“基础数据结构”(数组、链表、栈、哈希表、树)的高频题(如LeetCode 1、206、20、146、102),确保面试中“保底不丢分”;
- 再攻克“经典算法思想”(动态规划、回溯、双指针、贪心)的核心题(如LeetCode 416、46、15、55),这些是区分“基础扎实”和“能力突出”的关键;
- 最后针对性准备“工程实践题”(海量数据、图论应用、排序优化),适配中高级岗位需求,体现“算法落地能力”。
备考时需“一题多解”(如两数之和用暴力、双指针、哈希表三种方法,对比复杂度差异;反转链表用迭代和递归两种实现,分析空间消耗),并“多题归一”(如总结“滑动窗口的适用场景:子串/子数组的极值问题,如最长无重复子串、最小覆盖子串、滑动窗口最大值”;“单调栈的适用场景:找左右近邻的更大/更小元素,如接雨水、下一个更大元素、柱状图中最大的矩形”),才能在面试中应对自如,展现“基础扎实+思维灵活+工程落地”的算法工程师核心能力。