ARTICLE DETAIL

资讯详情

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

算法题总结274:面试前的高频题库与刷题攻略

算法题总结274:面试前的高频题库与刷题攻略 简介面向算法面试与职场进阶的经典题目整理文档适合正在准备大厂笔试、日常刷题巩固的开发者使用。资源以单个文档封装共一个文件大小约3.36MB便于离线阅读与检索。目前已有177人学习浏览。文档参考主流刷题平台与经典面试书系统整理高频考点并按数组、字符串、链表、树、栈和队列、数学、图、设计、海量数据及C/C基础等模块分类。各专题均围绕典型题目给出思路梳理例如数组中重复的数字、旋转数组的最小数字、二维数组中的查找、最长公共子序列、正则表达式匹配等既覆盖基础数据结构操作也涉及海量数据处理与系统设计片段。读者可按需跳转至薄弱章节在面试前集中回顾核心解法快速建立知识框架避免盲目刷题提升临场解题与代码手写能力。1. 算法题总结 274我为什么推荐把这份题库当面试前的“悔过书”算法题这东西总有那么一批人刷了三百道还是心里发虚也有另一批人拿着这份 274 题的中文答案解析一周时间就把核心考点过了一遍去面试时反而能清晰讲出思路。这份《算法题总结 274.pdf》我看下来最大的特点不是“题多”而是“题源干净”——题目来源集中在《剑指 offer》、LeetCode、LintCode、HihoCoder 和《王道程序员求职宝典》正好覆盖国内大厂和互联网公司技术面最爱问的那些原题变体。它把题目按数组、字符串、链表、树、栈和队列、数学、图、设计、海量数据、C/C 基础十个专题分类每个专题都有独立目录基本可以当一本“算法面试速查手册”用。适合谁两类人一类是马上要面试、需要短时间内过一遍高频题型的求职者另一类是刷题时老是卡在“知道思路但写不出来的”阶段需要一份带参考答案的源码包来做对比复盘的人。有了这份题库你缺的不是题而是“先做、再对、再总结”的闭环。2. 数据结构与算法面试的底层逻辑为什么刷题前先要懂考察点2.1 面试官到底在考察什么从“暴力枚举”到“最优解”的思维升级很多人在刷题时有一个误区以为面试官要看的是“你写出了正确答案”。实际上绝大部分面试官更在意的是你“怎么想到的”以及“从暴力解到最优解的推导过程”。一份好的题库价值恰恰在于覆盖这类推导路径。比如“数组中重复的数字”最基础的解法是用哈希表时间 O(n)空间 O(n)但面试官往往会追问如果要求 O(1) 空间怎么处理如果你看过这道题你就能答出“把值和下标对应不断交换”的套路这就是典型的“基于数组本身作为哈希表”的优化思路也是国内面试最常考的考点之一。我在面试别人时最常做的事是先让候选人说思路再问复杂度最后让他写代码。如果你刷题时只做了最后一步那你相当于把考试中最容易得分的过程分扔掉了。用这份题库练习时我建议你把每道题的“题目类型”先过一遍比如这题是考“查找、排序、动态规划、贪心、回溯还是位运算”再动手做。只有能判断每道题属于哪个考察维度你在面试中遇到陌生题时才不至于无从下手。2.2 数据结构的选型这道题用数组还是链表、栈还是队列从这份题库的目录可以看出数组和链表是绝对的重头戏占了将近 50 道题。数组类题目考察的是指针和下标操作、二分查找的边界条件、连续子数组的累积和与累积积、排列组合的去重、以及数组原地操作的能力。链表题则是另一个方向指针操作容易出现 null 指针引用、循环链表的判断、两个链表的公共节点、反转类问题这些都是高频考点。我的做法是每做完一道题在题目旁边标记“这道题如果换一种数据结构会不会更简单”。比如“用两个栈实现队列”——你如果用数组硬模拟当然也能做但如果在面试中提到栈的 push/pop 天然是倒序的就能自然引出“两个栈实现先进先出”的解法这种“结构决定解法”的思考习惯面试官更买账。同样像“包含 min 函数的栈”核心是用一个辅助栈记录当前栈内的最小值而不是每次调用 min 时去遍历一遍主栈——这种“空间换时间”的思路本身就是很多题目背后的通用解法。2.3 经典算法模式的归纳二分法、双指针、动态规划、剪枝这份题库的第 1 题到第 30 题之间几乎覆盖了最常见的算法模式。旋转数组的最小数字、旋转数组中查找数字、有序数组中查找数字的范围、数组中任一峰值的下标都属于二分法家族。它们的核心共同点是在“部分有序”的数组中我们总是能找到某种“中点判断”规则把搜索区间缩小一半。每次刷这类题我都建议用表格记录一下“mid 拿 left 还是 right、要不要加 1、循环条件写 left right 还是 left right”复盘时就一目了然。双指针则是数组类题的另一大模式比如“和为 s 的两个数字”、“求连续正数序列”、“盛最多水的容器”。这类题的共同套路是用两个指针从两端向中间收缩或一快一慢从同端前进。它比哈希表更节省空间时间复杂度又往往能达到 O(n)是面试中最好用、也最好讲的一类解法。动态规划在这个题库里的身影也不少正则表达式匹配、通配符匹配、最长公共子序列、最长公共子串、最长回文子串、单词拆分、多个字符串的最长公共前缀、数字字符串解码。这些都是面试中“动态规划族”的入门题特点是转移方程能背下来但写对初始化常常要靠经验。用这份题库练 DP 时我的习惯是先把状态定义写清楚再把 base case 给出来最后再写转移方程。顺序反了很容易在“边界”上翻车。2.4 常见算法题的复杂度对比模式典型题目时间复杂度空间复杂度适用条件暴力枚举数组中重复的数字O(n²)O(1)数据量小面试兜底方案哈希表两数之和O(n)O(n)不要求原地面试优先方案双指针盛最多水的容器O(n)O(1)数组有序或有“两端逼近”特征二分查找旋转数组的最小数字O(log n)O(1)数组部分有序能判断中点方向动态规划最长公共子序列O(mn)O(mn)有两段输入满足最优子结构位运算只出现 1 次的数字O(n)O(1)重复成对出现想找单独一个提示上面的复杂度对比是我自己在面试前复习时经常对着看的一张表。刷题时不要只追求 AC还要能把“最坏情况复杂度”和“空间是否能优化”讲出来。3. 把题库变成你的刷题计划按专题攻克的实操步骤3.1 第一步用两周时间建立“高频题优先级清单”拿到这份 PDF第一件事不是从头到尾看而是去“分析目录”。274 题看起来多但如果你按专题再看一遍就会发现数组类 30 道、链表类 16 道、字符串类 14 道、树类 21 道、栈和队列类 8 道、数学类约 20 道、图类 5 道、设计类 0 道题目较少、海量数据类 8 道、C/C 基础约 4 道。真正面试中出现频率最高的是数组、链表、树其次是字符串、数学、栈队列。我的两周刷题计划大致是第一周每天都只啃一个专题早上一小时过思路晚上一小时写代码第二周开始混合刷并且对第一周的薄弱专题做二次回刷。这样安排的好处是你可以在一个专题里看到“同一个套路被不同题目反复使用”比如“双指针”出现多次刷完数组再刷链表你就能总结出“凡是连续区间、相等元素、归并类问题先想双指针”的直觉。# 我自己用于刷题规划的一段简单脚本用来统计 PDF 中每个专题的题目数量 # 粗略提取每行中“数字-题目名”模式便于安排刷题顺序 import re content 数组中重复的数字 旋转数组的最小数字 旋转数组中查找数字 有序数组中查找数字的范围 缺失的数字 数组中数值和下标相等的元素 查找数组中任一峰值的下标 数组中只出现 1 次的 2 个数 数组中唯一只出现 1 次的数字 数据流的中位数 和为 s 的两个数字 和为 s 的连续正数序列 数组中出现次数超过一半的数字 数组中最小的 k 个数 连续子数组的最大和 连续子数组的最大积 正则表达式匹配 通配符匹配 两个字符串的最长公共子序列 两个字符串的最长公共子串 反转链表 复杂链表的复制 合并两个有序链表 重建二叉树 中序遍历的下一个节点 二叉树的最大路径和 用两个栈实现队列 斐波那契数列 丑数 二进制中 1 的个数 # 按空格拆词统计常见专题词出现的频率 wordlist content.split() freq {} for w in wordlist: if w in (数组, 链表, 字符串, 树, 栈, 队列, 数学, 图, 递归): freq[w] freq.get(w, 0) 1 for topic, n in sorted(freq.items(), keylambda x: -x[1]): print(f{topic}: {n})这段脚本的逻辑很简单把目录里的关键词按空格切开统计“数组、链表、字符串、树、栈、队列”等专题词出现了多少次输出一个粗略的专题题量排序。你不需要跑这个脚本只要照着这个思路去自己翻 PDF 的目录也能得出“数组 30 个、链表 16 个、树 21 个”这样的结论。关键是刷题前需要有优先级而不是从头刷到尾。3.2 第二步针对每个专题执行“读题-写思路-对答案”的闭环我复习时的执行办法是每道题先自己读题要求自己在 5 分钟内想到思路至少写出时间复杂度和空间复杂度。然后不看答案尝试直接在白纸上写代码先不管能不能跑通。写完再看 PDF 里的参考答案对比差异是“结构上的差异”还是“细节处理上的差异”。最后把这道题的“坑点”记在题目旁边比如“空链表”、“负数”、“整数溢出”、“中点和 left/right 的更新”。这样做看似费时但题目质量决定了它的价值——这些题基本都是面试原题或原题变体你练的不是“AC 能力”而是“面试表达能力”。比如“二进制中 1 的个数”这道题如果只写一个常规的循环右移面试官大概率会问“有没有 O(1) 的算法”如果你答出n (n - 1)不断消去最右边的 1面试官会觉得你是真正理解位运算的。3.3 第三步用题目标签反向建立“解题套路库”刷完一两周后你会发现自己记住了解法却没有记住题目的视觉特征。这时候建议做一件事把这份 PDF 中出现的题目按“解法套路”重新归类。比如凡是“有序数组中查找 xx”的必是二分变形题。凡是“从一个数组的所有子集中找 xxx”的大概率是回溯 剪枝。凡是“字符串匹配、公共子序列、编辑距离”的基本是二维 DP。凡是“连续子数组”的先想过一遍前缀和再看是简单累计还是 DP。我在刷这份题库时建了一个小表按套路分类。比如“数组”里“连续子数组的最大和”是一维 DP 的基础模型“连续子数组的最大积”则需要记录最大值和最小值因为负数相乘会反转大小关系。这些“分类学”不是题目本身给的而是刷完题后自己总结的但它才是面试现场回答“为什么这么解”的真正素材。// 以“连续子数组的最大积”为例核心是维护 max_cur 和 min_cur 两个变量 #include vector #include algorithm using namespace std; int maxProduct(vectorint nums) { if (nums.empty()) return 0; int max_cur nums[0], min_cur nums[0], ans nums[0]; for (int i 1; i nums.size(); i) { int tmp_max max_cur, tmp_min min_cur; // 当前元素若为负数乘以原来最大值会变成最小值乘以原来最小值会变成最大值 max_cur max({nums[i], tmp_max * nums[i], tmp_min * nums[i]}); min_cur min({nums[i], tmp_max * nums[i], tmp_min * nums[i]}); ans max(ans, max_cur); } return ans; }这个解法的核心在于数组里出现负数时简单的“记录最大积”失效必须同时记录最小积。由于max_cur和min_cur在更新时会相互依赖所以用tmp_max和tmp_min保存更新前的值。代码写法上要留意std::max({...})是 C11 的 initializer_list 写法如果你在面试中手写也可以用两两比较的方式但思路必须说明“为什么需要双记录”——这是面试官考察的点。3.4 第四步做一份自己的“高频错题清单”刷题过程中那些第一遍没写对的题才是你真正需要二刷的。我习惯用这样的记录方法题号把 PDF 里的题号抄下来方便查找。题型二分/DP/回溯/位运算/链表指针。错误类型边界没考虑 / 递归出口写错 / 复杂度不达标 / 思路完全卡住。一句话解法用自己能理解的话写出解法不是抄答案。比如“旋转数组的最小数字”第一遍做错的人非常多原因是把nums[mid] nums[right]的条件写成 ≥结果在重复元素出现时崩了。如果你在错题清单里写上“mid 与 right 比较若等于 right则 right-- 去重”那你下次遇到类似题就会比只看答案的人稳得多。3.5 真题实战演示从读题到 AC 的完整推演举个括号匹配的例子。题目“有效的括号”在题库第 144 题看起来是链表之外的独立小题但实际非常能考察栈的使用。核心思路是左括号入栈右括号出栈并校验匹配最后检查栈是否为空。def is_valid(s: str) - bool: stack [] pairs {): (, ]: [, }: {} for ch in s: if ch in pairs: if not stack or stack[-1] ! pairs[ch]: return False stack.pop() else: stack.append(ch) return not stack这段代码的关键是用了字典来映射右括号到左括号而不是三个 if 判断这样扩展性强也更容易写对。参数说明很简单s是输入字符串遍历时如果是右括号就看栈顶是否匹配如果是左括号就直接入栈。这个题还有两个变体一是“括号嵌套并包含加减乘除”的计算器——LeetCode 227/772那才是中高级面试题二是“括号的分数”或“移除无效括号”但思路都建立在栈这个基本功上。4. 链表、树与递归大部分人的分水岭都在这里4.1 链表题的指针陷阱null 判断、dummy 节点、反转链表题是新手最容易“看答案觉得简单、自己写就崩”的专题。原因只有一个指针操作涉及前置节点、当前节点、后置节点任何一个环节忘了保留引用链表就断了。这份题库里“反转链表”、“链表中间节点”、“链表中环的入口节点”、“删除链表中的节点”、“合并两个有序链表”都是最经典的题它们的共性解法是“三指针法”。以反转链表为例解法是prev null, cur head然后循环中先用next cur-next保存后置节点再把cur-next prev最后推进prev和cur。看似简单但面试中最常犯的错误是反转完头节点没有指向新的头部或者在循环结束后返回了cur而不是prev。刷链表题时最忌讳只看代码应该自己画一遍链表图每行代码对应一次指针变化。// 反转链表的非递归实现 ListNode* reverseList(ListNode* head) { ListNode* prev nullptr; ListNode* cur head; while (cur ! nullptr) { ListNode* next cur-next; // 先保存下一个节点防止断链 cur-next prev; // 当前节点指向前驱 prev cur; // prev 前移 cur next; // cur 前移 } return prev; // 循环结束时 prev 指向新链表头 }这里有个非常容易忽视的点最后的return prev而不是return cur。因为循环结束时cur已经变成nullptr而prev才是原来的尾节点也就是反转后的头节点。很多新手写第一遍时都会在这里翻车所以我会强调一遍链表题写完之后先手动跑一个单节点链表和三节点链表的用例确认返回值不是空指针。4.2 树的递归套路遍历、子树、路径和祖先问题二叉树问题在面试中的占比极高它的核心解法其实是递归。树的面试题无非几种模型遍历类先序、中序、后序、按层考察栈和队列的使用。路径类二叉树中和为某一值的路径、二叉树的最大路径和后两者考的是“从叶节点向上累加”。子树类镜像、对称、子结构这类题通常是“对每个节点递归判断”。构建类重建二叉树、二叉树转双向链表这类题要用“分治 中序/前序的边界计算”。拿“重建二叉树”来说题目给前序和中序遍历序列要求重建二叉树。核心是找到前序第一个节点就是根节点然后在中序里定位根节点的位置用来切分左子树和右子树。这个题的代码量不大但边界条件很多——中序区间为空时怎么办前序区间的起始下标怎么算这些细节第一次手写几乎都会错。// 根据前序和中序遍历重建二叉树 TreeNode* build(vectorint pre, int preL, int preR, vectorint vin, int vinL, int vinR) { if (preL preR || vinL vinR) return nullptr; TreeNode* root new TreeNode(pre[preL]); // 在中序序列中找根节点下标 int idx vinL; while (vin[idx] ! pre[preL]) idx; int leftLen idx - vinL; root-left build(pre, preL 1, preL leftLen, vin, vinL, idx - 1); root-right build(pre, preL leftLen 1, preR, vin, idx 1, vinR); return root; }这里的参数preL、preR、vinL、vinR是闭区间边界。每次递归时前序的左子树区间长度等于中序左子树的长度leftLen这个长度是从idx - vinL算出来的。如果中序里找不到根节点说明输入序列不合法但面试题默认输入是合法的如果前序区间和中序区间长度不一致也要直接返回空。做这类题先画一个 5 个节点的二叉树手动推一遍每组下标比对着代码看十遍都有效。4.3 递归转迭代什么时候改用栈或队列更稳很多树题用递归非常简洁但面试也喜欢问“能不能不用递归”。最典型的是中序遍历的非递归实现用栈模拟系统栈从根节点开始先把所有左孩子入栈然后弹出、访问、再转向右孩子。// 非递归中序遍历 vectorint inorderTraversal(TreeNode* root) { vectorint res; stackTreeNode* st; TreeNode* cur root; while (cur ! nullptr || !st.empty()) { if (cur ! nullptr) { st.push(cur); cur cur-left; // 一直往左走到底 } else { cur st.top(); st.pop(); res.push_back(cur-val); cur cur-right; // 左子树与根都处理完转向右子树 } } return res; }这个写法的好处是空间复杂度是 O(h)h 是树高而不是递归那样 O(n) 的调用栈。面试中如果题目要求“能否用迭代”你给出这个版本并且说清楚为什么比递归更省空间会是非常加分的点。树的按层遍历则是另一回事它需要用队列做 BFS如果题目要求“之字形打印”只要加一个层序奇偶判断就行。这份题库把这三题按层不分行、按层分行、之字形分别列出说明出题人很清楚“层层递进”的考察逻辑。5. 算法题避坑指南这五个坑几乎每个刷题的人都踩过5.1 二分查找的死循环不是所有 mid 都要mid left (right - left) / 2现象写旋转数组最小数字这类题时死循环或返回错误下标尤其在数组长度为 2、元素重复时特别容易重现。 原因当mid的更新策略与区间边界不一致时左边界永远不会前进。比如left mid时如果mid又等于 left则left永远不变。 解决统一使用mid left (right - left) / 2下取整并确保每次循环区间至少缩小一个元素——即left或right的移动必须发生在mid被排除之后。用while (left right)配合mid target时left mid 1、否则right mid这套模板能减少精神内耗。5.2 链表题的“断链”问题反转和删除时没保存后继节点现象把cur-next指向前一个节点之后原链表的后半段“丢了”或者遍历时指针越界。 原因修改cur-next前没有用临时变量保存cur-next的原始值。 解决写链表题时养成“动 next 之前先备份”的习惯。如果要删除某个节点也要考虑“尾部节点怎么处理”和“头节点怎么处理”两个边界。我一般会在代码开头写注释// next cur-next 必须放在修改 next 之前提醒自己。5.3 位运算骚操作的不适配场景n (n - 1)不是万能的现象做“二进制中 1 的个数”时直接用n (n - 1)统计结果在输入为负数时出错或者循环次数过多。 原因n (n - 1)对于负数比如-8会得到-16并不是简单消掉最右边的 1而且如果只向右移位负数的符号位会导致右移永远补 1形成死循环。 解决处理负数时要么先强转成无符号整数unsigned int要么右移时用逻辑右移Java 的C 则转unsigned再处理。这道题是很多大厂的高频题刷题时如果只背结论面试时被追问负数场景就会非常尴尬。5.4 动态规划的状态定义搞反先写转移方程后回填 base case现象很多人在做“最长公共子序列”时dp[i][j] 定义成“以 i 结尾的最长公共子序列”结果在字符不相等时的转移方程完全写不出来。 原因状态定义选择错误。正确的是 dp[i][j] 表示“第一个字符串前 i 个字符与第二个字符串前 j 个字符的最长公共子序列长度”而不是“以 i 结尾”。 解决先定义状态、再写转移方程、再填 base case。char 相等时dp[i][j] dp[i-1][j-1] 1不相等时dp[i][j] max(dp[i-1][j], dp[i][j-1])。如果你发现某个状态定义导致转移方程出现“既要又要”大概率是定义错了。5.5 海量数据题的“伪最优”位图与布隆过滤器分不清边界现象做“某个数是否在 40 亿个数中”、“2.5 亿整数中只出现一次的整数”这类题时第一反应是排序或哈希但面试官追问内存限制时答不上来。 原因位图和布隆过滤器是海量数据题的常见解法但它们的区别是位图精确但范围固定布隆过滤器是“一定概率误判”适合判断“肯定不在”、无法判断“一定在”。 解决看到“40 亿个数查是否存在”时用 2^32 位512 MB的位图看到“多个大文件去重”且允许少量误判时用布隆过滤器看到“统计 URL 重复次数”时就要考虑哈希分桶而不是位图。刷这类题时要自己多问一个“空间限制是多少、是否允许误差”答案往往就在这两个条件里。# 位图实现示例适合用在“40亿个数中查某个数是否存在” class Bitmap: def __init__(self, size: int): self.size size self.bits [0] * ((size 31) // 32) def set(self, num: int): idx, offset num // 32, num % 32 self.bits[idx] | (1 offset) def test(self, num: int) - bool: idx, offset num // 32, num % 32 return (self.bits[idx] offset) 1 # 假设有 40 亿个数用 int 列表模拟位图 bm Bitmap(2**32) arr [123456, 888888, 123456789] for v in arr: bm.set(v) print(bm.test(888888)) # True print(bm.test(999999)) # False这个位图的实现用每个比特位表示一个数的存在性内存开销是 n/8 字节。当整数范围是 0~2^32 时2^32/8 字节约 512MB对现代服务器来说并不是不能接受。它的最大优点是查找时间是 O(1)且不需要额外的哈希表结构但缺点是只能判断存在与否不能统计频次。遇到“只出现一次”的变体就要用“两个位图分别记录出现零次、一次、多次”的扩展方法。6. 把 274 题变成你自己的算法笔记模板记忆法与三刷验证法6.1 用“模板记忆法”替代题海战术刷题到最后拼的不是“见过多少题”而是“能快速把新题归入哪一类”。我建议在把这份题库刷完之后做一个动作把每个专题里的题目压缩成“一句话模板”。二叉树递归处理左右子树返回值和参数决定能否剪枝。 链表三指针遍历删除类问题加 dummy 节点。 字符串先判断是子串还是子序列再用双指针或 DP。 数学先想位运算、幂运算再想数学推导。 海量数据先问内存限制再决定位图、分桶、布隆过滤器。比如你见到“合并 k 个有序链表”第一反应应该是“每次取 k 个头节点最小的节点用一个大小为 k 的小顶堆维护”而不是“从头开始逐个比较”。这份 PDF 的目录里恰好覆盖了合并两个有序链表和合并 k 个有序链表前者是递归或双指针后者是小顶堆。如果刷完两道题后你能得出“k 路归并这个套路”那才叫把题刷透了。模板记忆法的核心是不是为了背诵模板而是为了在看到题目特征时第一时间想到“这是哪一类”这比临时推导节省大量时间也能提升思路的完整度。6.2 三刷验证法一刷题型、二刷代码、三刷思考我自己在实际准备面试时会把一份资料刷三遍每遍目的都不同。第一遍只做“题型归类”看每道题的标题能在 10 秒内说出“这道题要考察什么”做到这一步你会对面试题有整体把握。第二遍是“代码实现关”每道题只写一次写完过用例并把卡住的地方记录下来。第三遍在面试前一两天做只看自己的笔记错题清单 模板库 复杂度表格快速在心里默述每道题的解法不再重新写代码。这样的三刷安排能够克服“看着答案觉得很简单、自己写还是不会”的困境。尤其是第二遍写不出来并不可耻真正宝贵的是把自己卡住的那个点记下来——面试官最喜欢追问的正好就是这些“卡点”。职场面试不是考试不求你会做多少题而求你能把已经掌握的题目清晰地讲出来。6.3 面试现场的时间分配技巧面试时遇到算法题我建议先用 1~2 分钟确认题意用“输入是否可能为空、数字是否可能为负、重复元素是否考虑”这三个问题堵住题目的边界。再用 2~3 分钟说思路和复杂度最后才动笔写代码。如果写代码过程中卡住了不要沉默要和面试官交流“我现在卡在边界处理上”这不会减分反而体现沟通能力。在刷这份题库时我最大的教训是不要做无脑“AC 机器”。二刷时遇到做过的题应该先默写思路再写代码。通读答案的人往往是“看了答案——写一遍——过了——忘了”过两周再来还是不会。真正有效的是自己做不出来之后再看答案然后在错题清单里写下“我卡在哪一步”过一周再做一遍同样题型。从那以后我每次面试前都会强制自己走一遍“一看目录归题型二看错题清单三默述复杂度”的流程每次都至少能稳定发挥出自己刷过题的八成水平。希望这套方法也能帮你在准备算法面试时少走弯路。本文还有配套的精品资源点击获取
返回列表