以下用一场 “算法王国闯关挑战赛” 的故事,结合 Java代码 和 高频题型分类,带你通关大厂面试最常考的算法题!每个题型附核心思路、代码模板和实战例题(数据来自2024年大厂真题统计)👇
🏔️ 第一关:数组山谷(数组与字符串)
剧情:你是一名登山者,在数字山谷中寻找宝藏(目标值),但山路崎岖(数据无序),还有毒蛇(重复元素)挡路!
高频题:
-
两数之和(出现频率:90%)
- 故事:找到两座山峰高度和等于目标值,避免跌落悬崖。
- 解法:哈希表记录已路过山峰高度,遇新峰时查补数。
java Copy public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[]{map.get(complement), i}; } map.put(nums[i], i); } return new int[0]; } // 时间复杂度:O(n),空间:O(n) [1](@ref)
-
滑动窗口最大值(出现频率:70%)
- 故事:在狭窄山路上,你只能看到连续K步内的最高峰。
- 解法:单调队列维护窗口内递减序列。
java Copy public int[] maxSlidingWindow(int[] nums, int k) { Deque<Integer> deque = new ArrayDeque<>(); int[] res = new int[nums.length - k + 1]; for (int i = 0; i < nums.length; i++) { // 队尾小于当前值则弹出 while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { deque.pollLast(); } deque.offerLast(i); // 队首是否在窗口外 if (deque.peekFirst() <= i - k) deque.pollFirst(); // 记录窗口最大值 if (i >= k - 1) res[i - k + 1] = nums[deque.peekFirst()]; } return res; } // 时间:O(n),空间:O(k) [1,3](@ref)
🌲 第二关:链表森林(链表操作)
剧情:穿越迷雾森林,藏宝图(链表)被加密,有的路线成环(循环链表),有的需要翻转(反转链表)!
高频题:
-
反转链表(出现频率:85%)
- 故事:将藏宝图路线从尾到头重新连接。
- 解法:三指针轮转(prev, cur, next)。
java Copy public ListNode reverseList(ListNode head) { ListNode prev = null, cur = head; while (cur != null) { ListNode next = cur.next; cur.next = prev; // 翻转箭头 prev = cur; // 前移prev cur = next; // 前移cur } return prev; // 新头节点 } // 时间:O(n),空间:O(1) [2,6](@ref)
-
环形链表检测(出现频率:80%)
- 故事:森林中有环形陷阱,用快慢指针逃离(快指针走2步,慢指针走1步)。
java Copy public boolean hasCycle(ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; } return false; } // 时间:O(n),空间:O(1) [6,7](@ref)
🧩 第三关:动态规划迷宫(DP问题)
剧情:迷宫中的金币(子问题)分散分布,你要在守卫(约束条件)巡逻下拿最多金币!
高频题:
-
背包问题(出现频率:75%)
- 故事:背包容量有限,每块金币重量和价值不同,最大化总价值。
- 状态方程:
dp[i][w] = max(不拿第i个, 拿第i个 + dp[i-1][w-weight[i]])
java Copy public int knapSack(int W, int[] wt, int[] val) { int n = wt.length; int[][] dp = new int[n + 1][W + 1]; for (int i = 1; i <= n; i++) { for (int w = 1; w <= W; w++) { if (wt[i-1] <= w) { dp[i][w] = Math.max( dp[i-1][w], val[i-1] + dp[i-1][w - wt[i-1]] ); } else { dp[i][w] = dp[i-1][w]; } } } return dp[n][W]; } // 时间:O(n*W),空间:O(n*W) [5](@ref)
-
股票买卖(出现频率:70%)
- 故事:每天宝石价格波动,低买高卖赚差价(可多次交易)。
- 贪心解法:只要今天比昨天涨就卖出!
java Copy public int maxProfit(int[] prices) { int profit = 0; for (int i = 1; i < prices.length; i++) { if (prices[i] > prices[i-1]) { profit += prices[i] - prices[i-1]; } } return profit; } // 时间:O(n),空间:O(1) [5](@ref)
🌳 第四关:算法村庄(树与图)
剧情:村庄道路成树状,找最短送信路径(二叉树遍历),或识别家族关系网(图遍历)。
高频题:
-
二叉树层序遍历(出现频率:80%)
- 故事:按家族辈分从上到下统计人口。
- 解法:队列实现BFS。
java Copy public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); if (root == null) return res; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { int size = queue.size(); List<Integer> level = new ArrayList<>(); for (int i = 0; i < size; i++) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null) queue.offer(node.left); if (node.right != null) queue.offer(node.right); } res.add(level); } return res; } // 时间:O(n),空间:O(n) [2,4](@ref)
-
图的最短路径(Dijkstra算法)
- 故事:用魔法飞毯(优先队列)快速找到村庄间最短路径。
java Copy public int[] dijkstra(int[][] graph, int start) { int n = graph.length; int[] dist = new int[n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[start] = 0; PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]); pq.offer(new int[]{start, 0}); while (!pq.isEmpty()) { int[] node = pq.poll(); int u = node[0]; for (int v = 0; v < n; v++) { if (graph[u][v] != 0) { int newDist = dist[u] + graph[u][v]; if (newDist < dist[v]) { dist[v] = newDist; pq.offer(new int[]{v, newDist}); } } } } return dist; }
🔍 第五关:村长的特别挑战(其他高频题)
-
二分查找(出现频率:85%)
- 故事:村长把宝藏钥匙藏在有序书页中,快速定位页码。
java Copy 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; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1; } // 时间:O(log n),空间:O(1) [3,5](@ref)
-
贪心算法:种花问题(出现频率:60%)
- 故事:在花坛中隔一个坑种一朵花,最大化种植数。
java Copy public boolean canPlaceFlowers(int[] flowerbed, int n) { int count = 0; for (int i = 0; i < flowerbed.length && count < n; i++) { if (flowerbed[i] == 0 && (i == 0 || flowerbed[i-1] == 0) && (i == flowerbed.length-1 || flowerbed[i+1] == 0)) { flowerbed[i] = 1; count++; } } return count >= n; } // 时间:O(n),空间:O(1) [3](@ref)
💎 高频算法题总结表(大厂面试Top 10)
题型 | 高频题目 | 出现频率 | 核心思想 |
---|---|---|---|
数组/字符串 | 两数之和、滑动窗口最大值 | 90% | 哈希表、单调队列 |
链表 | 反转链表、环形链表 | 85% | 三指针、快慢指针 |
动态规划 | 背包问题、股票买卖 | 80% | 状态转移方程、空间优化 |
二叉树 | 层序遍历、重建二叉树 | 75% | BFS、分治法 |
二分查找 | 搜索旋转数组、找元素边界 | 70% | 红蓝指针法 |
贪心算法 | 种花问题、区间调度 | 65% | 局部最优解 |
栈/队列 | 最小栈、用栈实现队列 | 60% | 双栈协作 |
堆 | Top K 元素、合并K个链表 | 55% | 优先队列 |
位运算 | 只出现一次的数字 | 50% | 异或性质 |
设计题 | LRU缓存、循环队列 | 45% | 哈希表+双向链表 |
🧠 通关秘籍
-
先判题型:
- 最值问题 → 想 贪心 或 DP
- 子数组/子串 → 滑动窗口 或 前缀和
- 有序数据 → 二分查找
- 树/图路径 → DFS/BFS
-
代码模板化:
- 链表题:虚拟头节点 + 双指针
- DP题:定义状态 + 转移方程 + 初始化
- 树题:递归终止条件 + 左右子树处理
-
复杂度敏感:
- 10^5 数据规模 → 必须 O(n log n) 以下解法
- 看到“有序” → 想能否二分优化到 O(log n)
⚡ 终极建议:从 LeetCode Hot 100 和 《剑指Offer》 开始刷,每天2题坚持2个月,80%面试题可覆盖!遇到难题时默念:“分治拆解子问题,指针滑动空间换” —— 算法面试,不过如此!