1. 双指针的三种形态?

原地去重(快慢指针,经典)
// 有序数组原地去重,返回新长度
public int removeDuplicates(int[] a) {
    int slow = 0;
    for (int fast = 1; fast < a.length; fast++)
        if (a[fast] != a[slow]) a[++slow] = a[fast];   // slow 指向已去重末尾
    return slow + 1;
}

🎯 面试要点

  • 双指针的核心收益:把 O(n²) 暴力降为 O(n)——"排序 + 双指针"是数组题万能起点
  • 对撞指针要求有序性(自己排序或题目有序);快慢指针注意 null/边界

2. 滑动窗口模板?(子串/子数组题万能)

适用:连续子串/子数组问题——最长/最短满足条件的窗口。核心:右指针扩展,窗口不满足条件时收缩左指针。

滑动窗口通用框架
int left = 0, len = 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);  // 出窗口
        if (window.get(d) == 0) window.remove(d);
    }

    len = Math.max(len, right - left + 1);   // ③ 更新答案(最长/最短不同写法)
}
return len;

🎯 面试要点

  • "最长无重复子串"、"最小覆盖子串"、"字符串排列"都是这模板;收缩条件是每题的差异点
  • 窗口计数用数组(int[26] 或 int[128])比 HashMap 更快

3. 前缀和与差分?

和为 k 的子数组个数(前缀和 + Map)
public int subarraySum(int[] a, int k) {
    Map<Integer, Integer> count = new HashMap<>();
    count.put(0, 1);                      // pre=0 出现 1 次(空前缀)
    int pre = 0, ans = 0;
    for (int x : a) {
        pre += x;
        ans += count.getOrDefault(pre - k, 0);  // 之前出现过 pre-k 的次数
        count.merge(pre, 1, Integer::sum);
    }
    return ans;
}

🎯 面试要点

  • 区间和/计数问题先想前缀和;二维版:prefix[i][j] = 区域和,容斥公式
  • "pre - k 出现过多少次"是计数型题目的核心转化

4. 位运算技巧 & 单调栈?

位运算:

单调栈:栈内元素保持单调(递增/递减)。典型应用:下一个更大元素、每日温度、柱状图最大矩形。模板:遍历时维护单调栈,出栈时结算答案。

下一个更大元素(单调递减栈)
public int[] nextGreater(int[] a) {
    int[] res = new int[a.length];
    Deque<Integer> stack = new ArrayDeque<>();   // 存下标
    for (int i = a.length - 1; i >= 0; i--) {   // 从右往左
        while (!stack.isEmpty() && a[stack.peek()] <= a[i]) stack.pop();
        res[i] = stack.isEmpty() ? -1 : a[stack.peek()];
        stack.push(i);
    }
    return res;
}

🎯 面试要点

  • 单调栈的直觉:栈里存"还没找到答案的候选",弹出即结算
  • 循环数组版(下一个更大元素 II):数组拉长两倍取模即可
  • 位运算面试量少但考到要秒答:异或去重、与消位是最常考两个

🎤 常见面试追问

  1. 双指针什么时候能用?——数组有序/找配对/原地操作类问题。本质是"用有序性减少重复扫描",把 O(n²) 降到 O(n)。
  2. 滑动窗口的收缩条件怎么定?——窗口"不满足题目约束"时就收缩左指针,直到重新满足;答案在每次调整后更新。每题的差异点就是"约束是什么"。
  3. 前缀和能解决什么类型的问题?——"子数组区间和/区间计数"类问题:O(1) 求任意区间和,配哈希可以 O(n) 求"和为 k 的子数组个数"。
  4. 单调栈的直觉是什么?——栈里存"还没找到答案的候选元素",当新元素让栈顶"失去候选资格"(如更大元素出现)时出栈结算答案。适合"下一个更大/更小元素"。
  5. 位运算有什么实际用途?——状态压缩(用 int 位表示开关)、找唯一出现一次的数(异或)、快速判奇偶、集合运算优化(哈希的底层就是位运算)。

📖 名词解释(本页术语)

术语 大白话解释
双指针用两个下标/引用遍历,分对撞(两头向中间)、快慢(速度不同)、同向(一起走)三种形态,常把 O(n²) 优化到 O(n)。
滑动窗口同向双指针维护一个连续区间(窗口),右扩左缩,解决"最长/最短满足条件的子数组/子串"。
前缀和预处理数组 pre[i] = 前 i 个元素之和,之后任意区间 [l, r) 的和 = pre[r] - pre[l],O(1) 查区间。
差分数组记录相邻元素的差,用于"多次给区间统一加值"后一次性还原(O(1) 改区间,O(n) 还原)。
位运算直接操作二进制位的运算:&(与)、|(或)、^(异或)、~(取反)、<</>>(移位)。速度快,能表示状态集合。
异或(^)相同为 0、不同为 1。特性:x^x=0、x^0=x——所以"其他都成对、只有一个单身"的数组用异或一遍就能找出它。
单调栈栈内元素保持单调递增/递减的栈,出栈时结算答案。典型题:下一个更大元素、每日温度、柱状图最大矩形。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。