好的!我用一个「算法武林大会」的故事,结合 10 道高频算法题,帮你快速掌握面试必考题!
一、故事背景:算法武林大会
场景:
你参加一场算法武林大会,要通过 10 道关卡才能成为武林盟主!每道关卡对应一种经典算法题。
二、高频算法题分类及解法
1. 二分查找(LeetCode 704)
关卡描述:
在排序数组 [1,3,5,7,9]
中找到数字 7
的位置。
解题思路:
-
每次将数组分成两半,比较中间值与目标值的大小,缩小查找范围。
Java 代码:
java
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) return mid;
if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1; // 未找到
}
2. 双指针(LeetCode 11)
关卡描述:
给定数组 [1,8,6,2,5,4,8,3,7]
,找到能容纳最多水的两个柱子之间的区域。
解题思路:
-
左右指针分别指向数组两端,每次移动较短的柱子,计算面积并更新最大值。
Java 代码:
java
public int maxArea(int[] height) {
int left = 0, right = height.length - 1;
int maxArea = 0;
while (left < right) {
int area = Math.min(height[left], height[right]) * (right - left);
maxArea = Math.max(maxArea, area);
if (height[left] < height[right]) left++;
else right--;
}
return maxArea;
}
3. 滑动窗口(LeetCode 3)
关卡描述:
找到字符串 "abcabcbb"
中最长无重复字符的子串长度。
解题思路:
-
用哈希表记录字符出现的位置,右指针扩展窗口,左指针在重复时收缩。
Java 代码:
java
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (map.containsKey(c) && map.get(c) >= left) {
left = map.get(c) + 1;
}
map.put(c, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
4. 回溯算法(LeetCode 78)
关卡描述:
求数组 [1,2,3]
的所有子集。
解题思路:
-
递归遍历每个元素,选择或不选择该元素,生成所有可能的子集。
Java 代码:
java
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
backtrack(nums, 0, new ArrayList<>(), result);
return result;
}
private void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> result) {
result.add(new ArrayList<>(path)); // 保存当前子集
for (int i = start; i < nums.length; i++) {
path.add(nums[i]); // 选择当前元素
backtrack(nums, i + 1, path, result); // 递归
path.remove(path.size() - 1); // 撤销选择
}
}
5. 动态规划(LeetCode 53)
关卡描述:
求数组 [-2,1,-3,4,-1,2,1,-5,4]
的最大子数组和。
解题思路:
-
定义
dp[i]
为以第i
个元素结尾的最大子数组和,状态转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])
。
Java 代码:
java
public int maxSubArray(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
dp[0] = nums[0];
int max = dp[0];
for (int i = 1; i < n; i++) {
dp[i] = Math.max(nums[i], dp[i-1] + nums[i]);
max = Math.max(max, dp[i]);
}
return max;
}
6. 二叉树遍历(LeetCode 94)
关卡描述:
中序遍历二叉树 [1,null,2,3]
。
解题思路:
-
递归遍历左子树 → 访问根节点 → 递归遍历右子树。
Java 代码:
java
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inorder(root, result);
return result;
}
private void inorder(TreeNode root, List<Integer> result) {
if (root == null) return;
inorder(root.left, result); // 左
result.add(root.val); // 根
inorder(root.right, result); // 右
}
7. 图的 BFS(LeetCode 102)
关卡描述:
层序遍历二叉树 [3,9,20,null,null,15,7]
。
解题思路:
-
用队列实现 BFS,每层节点入队,处理完一层后将子节点入队。
Java 代码:
java
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
currentLevel.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(currentLevel);
}
return result;
}
8. 贪心算法(LeetCode 55)
关卡描述:
数组 [2,3,1,1,4]
,每个元素表示最大跳跃长度,判断是否能跳到最后一个位置。
解题思路:
-
维护一个最远可达位置
maxReach
,遍历数组更新maxReach
,如果maxReach
能到达末尾则返回true
。
Java 代码:
java
public boolean canJump(int[] nums) {
int maxReach = 0;
for (int i = 0; i < nums.length; i++) {
if (i > maxReach) return false; // 无法到达当前位置
maxReach = Math.max(maxReach, i + nums[i]);
if (maxReach >= nums.length - 1) return true; // 已到达末尾
}
return false;
}
9. 排序算法(LeetCode 215)
关卡描述:
找到数组 [3,2,1,5,6,4]
的第 2 大元素。
解题思路:
-
用快速选择算法(类似快排),平均时间复杂度 O (n)。
Java 代码:
java
public int findKthLargest(int[] nums, int k) {
int targetIndex = nums.length - k; // 第 k 大 = 第 (n-k) 小
return quickSelect(nums, 0, nums.length - 1, targetIndex);
}
private int quickSelect(int[] nums, int left, int right, int targetIndex) {
if (left == right) return nums[left];
int pivotIndex = partition(nums, left, right);
if (pivotIndex == targetIndex) return nums[pivotIndex];
else if (pivotIndex > targetIndex) return quickSelect(nums, left, pivotIndex - 1, targetIndex);
else return quickSelect(nums, pivotIndex + 1, right, targetIndex);
}
private int partition(int[] nums, int left, int right) {
int pivot = nums[right];
int i = left - 1;
for (int j = left; j < right; j++) {
if (nums[j] <= pivot) {
i++;
swap(nums, i, j);
}
}
swap(nums, i + 1, right);
return i + 1;
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
10. 位运算(LeetCode 136)
关卡描述:
数组 [4,1,2,1,2]
中,只有一个元素只出现一次,其他元素都出现两次,找出这个元素。
解题思路:
-
利用异或运算
^
的性质:a ^ a = 0
,a ^ 0 = a
,遍历数组异或所有元素。
Java 代码:
java
public int singleNumber(int[] nums) {
int result = 0;
for (int num : nums) {
result ^= num; // 异或运算
}
return result;
}
三、高频算法题分类总结
类别 | 核心思想 | 典型题目 |
---|---|---|
二分查找 | 分治,缩小搜索范围 | 有序数组找目标值 |
双指针 | 两端向中间逼近或快慢指针 | 盛最多水的容器、链表环 |
滑动窗口 | 动态调整窗口大小 | 无重复字符的最长子串 |
回溯算法 | 递归遍历所有可能 | 子集、排列组合 |
动态规划 | 拆分子问题,记录中间结果 | 最大子数组和、背包问题 |
树与图遍历 | BFS/DFS | 二叉树层序遍历、图的最短路径 |
贪心算法 | 每步选最优,局部最优到全局 | 跳跃游戏、活动选择 |
排序与选择 | 快排、归并、堆排序 | 第 K 大元素、数组排序 |
位运算 | 利用位运算特性 | 只出现一次的数字 |
四、面试准备建议
-
掌握基础算法:二分、双指针、DFS/BFS、动态规划。
-
多刷题:从高频题开始,推荐 LeetCode 前 200 题。
-
分析复杂度:时间 / 空间复杂度是面试重点。
-
手写代码:练习白板编程,注意边界条件和代码风格。
记忆口诀:
- 算法武林有十招,二分双指针滑动窗,回溯动规加贪心,树图排序位运算!