面试知识库
极高 进阶

校招高频算法题型与变种#

一句话答案#

校招算法面试 80% 的题可归入 10 种模式:双指针、滑动窗口、二分查找、BFS/DFS、回溯、动态规划、单调栈/队列、堆/TopK、前缀和/差分、并查集,识别模式比死记题目更重要。

核心要点

一、十大题型模式映射表

#模式识别信号代表题复杂度
1双指针有序数组、回文、链表相交/环两数之和II、三数之和、接雨水O(n)
2滑动窗口连续子数组/子串、最值、定长/变长最小覆盖子串、无重复最长子串、长度最小子数组O(n)
3二分查找有序/单调性、最小化最大值、查边界搜索旋转数组、寻找峰值、Koko吃香蕉O(log n)
4BFS/DFS图/树遍历、最短路径、连通分量岛屿数量、二叉树层序遍历、课程表O(V+E)
5回溯排列/组合/子集、棋盘类全排列、N皇后、组合总和、电话号码字母组合指数级
6动态规划最优子结构、重叠子问题、计数/最值最长递增子序列、编辑距离、零钱兑换、背包问题O(n²)或O(n×m)
7单调栈/队列下一个更大/更小、滑动窗口最值每日温度、柱状图最大矩形、滑动窗口最大值O(n)
8堆/TopK第K大/小、合并K个有序、流式中位数前K个高频元素、合并K个排序链表、数据流中位数O(n log k)
9前缀和/差分区间和查询、子数组和等于K和为K的子数组、区间加法、航班预订统计O(n)
10并查集连通性判断、集合合并冗余连接、账户合并、最长连续序列O(α(n))≈O(1)

二、各模式核心模板(Java)

双指针——对撞型

int left = 0, right = nums.length - 1;
while (left < right) {
    int sum = nums[left] + nums[right];
    if (sum == target) return new int[]{left, right};
    else if (sum < target) left++;
    else right--;
}
java

滑动窗口——变长窗口

int left = 0;
Map<Character, Integer> window = new HashMap<>();
for (int right = 0; right < s.length(); right++) {
    char c = s.charAt(right);
    window.merge(c, 1, Integer::sum);
    while (/* 窗口需要收缩的条件 */) {
        char d = s.charAt(left);
        window.merge(d, -1, Integer::sum);
        left++;
    }
    // 更新答案
}
java

二分查找——左边界

int left = 0, right = nums.length; // 注意右边界
while (left < right) {
    int mid = left + (right - left) / 2;
    if (nums[mid] < target) left = mid + 1;
    else right = mid; // 收缩右边界找第一个>=target
}
return left;
java

BFS 模板

回溯模板

void backtrack(List<List<Integer>> res, List<Integer> path, int[] nums, int start) {
    if (/* 满足条件 */) { res.add(new ArrayList<>(path)); return; }
    for (int i = start; i < nums.length; i++) {
        if (/* 剪枝条件 */) continue;
        path.add(nums[i]);
        backtrack(res, path, nums, i + 1); // 组合用i+1,排列用0+visited
        path.remove(path.size() - 1);      // 撤销选择
    }
}
java

动态规划——框架

// 1. 定义状态:dp[i] = 以nums[i]结尾的最优解
// 2. 状态转移:dp[i] = max/min(dp[j] + ...) for all valid j
// 3. 初始化:dp[0] = base case
// 4. 遍历顺序:确保计算dp[i]时所依赖的dp[j]已经算过
// 5. 返回值:dp[n-1] 或 max(dp[0..n-1])
java

单调栈——下一个更大元素

Deque<Integer> stack = new ArrayDeque<>();
int[] res = new int[nums.length];
Arrays.fill(res, -1);
for (int i = 0; i < nums.length; i++) {
    while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
        res[stack.pop()] = nums[i];
    }
    stack.push(i);
}
java

三、变种识别技巧

原题变种关键差异
两数之和(哈希)三数之和(排序+双指针)增加一层循环+去重
二分查找精确值二分查找左/右边界while条件和边界更新不同
岛屿数量(DFS)岛屿最大面积DFS返回面积并累加
全排列(无重复)全排列II(有重复)先排序+跳过相邻重复
0-1背包完全背包内层循环正序 vs 倒序
LRU缓存LFU缓存增加频率维度+双哈希
最长递增子序列O(n²)最长递增子序列O(nlogn)patience sorting + 二分

四、高频校招真题清单(Top 30)

难度题目模式出现频率
Easy两数之和哈希★★★★★
Easy反转链表链表★★★★★
Easy有效括号★★★★
Easy合并两个有序链表链表★★★★
Easy二叉树最大深度DFS★★★★
Medium三数之和双指针★★★★★
Medium无重复最长子串滑动窗口★★★★★
MediumLRU缓存哈希+链表★★★★★
Medium二叉树层序遍历BFS★★★★
Medium全排列回溯★★★★
Medium零钱兑换DP★★★★
Medium最长递增子序列DP/二分★★★★
Medium岛屿数量DFS★★★★
Medium搜索旋转排序数组二分★★★★
Medium每日温度单调栈★★★
Medium合并区间排序★★★
Medium课程表拓扑排序★★★
Medium前K个高频元素★★★
Medium最小覆盖子串滑动窗口★★★
Medium最长回文子串双指针/DP★★★
Medium和为K的子数组前缀和★★★
Medium最小路径和DP★★★
Medium组合总和回溯★★★
Hard接雨水双指针/单调栈★★★★
Hard合并K个排序链表★★★
Hard滑动窗口最大值单调队列★★★
Hard编辑距离DP★★★
Hard柱状图最大矩形单调栈★★
Hard数据流中位数双堆★★
HardN皇后回溯★★

五、手写注意事项

  • 边界处理:空数组、单元素、全相同元素、Integer溢出(用 longmid = left + (right-left)/2
  • 循环不变量:明确定义 [left, right] 还是 [left, right) 并全程保持一致
  • 返回值:确认返回索引还是值、是否需要排序/去重
  • 复杂度分析:主动说出时间和空间复杂度
  • 测试用例:写完后用 1-2 个边界 case 手动跑一遍
面试回答(2分钟版)

我准备算法的方式是按模式分类刷题,把常见题归入十种模式:双指针、滑动窗口、二分、BFS/DFS、回溯、DP、单调栈、堆、前缀和、并查集。面试时我先识别题目属于哪种模式再套模板。比如看到连续子数组求最值就想到滑动窗口,看到有序或单调性就想二分,看到排列组合就想回溯。每种模式我都有一个核心模板,变种只是在模板基础上改条件或加约束。比如全排列有重复元素的变种就是先排序再跳过相邻重复。手写代码时我特别注意三件事:边界处理确保不越界,循环不变量保持一致,写完后用边界case手动验证。

追问与易错

追问方向:

  • “滑动窗口什么时候用定长什么时候用变长?”→ 定长:求定长子数组最值;变长:求满足条件的最短/最长子数组
  • “DP 怎么判断用一维还是二维?”→ 看状态依赖几个维度:一个序列一维,两个序列或矩阵二维
  • “回溯怎么剪枝?”→ 排序后跳重复、提前判断不可能满足条件、记忆化
  • “什么时候用 BFS 什么时候用 DFS?”→ 最短路径/层序用BFS,路径搜索/连通性/树遍历用DFS
  • “时间复杂度怎么快速分析?”→ 看循环嵌套层数、递归树深度×宽度、是否有二分/排序

易错点:

  • ❌ 二分查找 mid = (left+right)/2 溢出——用 left + (right-left)/2
  • ❌ 回溯忘记撤销选择——每次 add 后必须有对应 remove
  • ❌ 滑动窗口忘记收缩——while 条件写错导致窗口只扩不缩
  • ❌ DP 初始化不完整——忘了 dp[0] 或 dp[0][j] 的 base case
  • ❌ DFS 忘记标记已访问——导致死循环或重复计算
  • ✅ 写完代码主动说复杂度+跑边界case,面试官印象分很高