🚀 5-7天Java算法速成计划 | 互联网大厂高频题全解析
算法是Java后端面试的必考项!无论是腾讯、阿里还是字节,高频算法题都是核心考察点。本文整理了11大高频算法题型,结合代码案例和考点分析,助你快速掌握核心逻辑,轻松应对面试!
📅 Day 1:栈与队列
🔥 题目:用两个栈实现队列(Push/Pop操作)
考点:栈的特性(FILO)与队列(FIFO)的转换逻辑。
代码:
public class MyQueue {
private Stack<Integer> stack1; // 入队栈
private Stack<Integer> stack2; // 出队栈
public MyQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
// 入队操作:直接压入stack1
public void push(int x) {
stack1.push(x);
}
// 出队操作:
public int pop() {
// 若stack2为空,将stack1所有元素倒入stack2
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop(); // 弹出stack2栈顶(即队列首元素)
}
}
关键逻辑:
push()
直接操作stack1
,时间复杂度O(1)。pop()
时,若stack2
为空,则将stack1
元素全部倒入stack2
,此时stack2
栈顶即队列首元素。
📅 Day 2:排序算法
🔥 题目:快速排序(QuickSort)
考点:分治思想、分区逻辑、时间复杂度分析。
代码:
public class QuickSort {
public static void sort(int[] arr, int left, int right) {
if (left < right) {
int pivotIndex = partition(arr, left, right); // 分区
sort(arr, left, pivotIndex - 1); // 左半部分递归
sort(arr, pivotIndex + 1, right); // 右半部分递归
}
}
private static int partition(int[] arr, int left, int right) {
int pivot = arr[right]; // 选择最后一个元素为基准
int i = left; // 标记小于基准的边界
for (int j = left; j < right; j++) {
if (arr[j] < pivot) {
swap(arr, i, j);
i++;
}
}
swap(arr, i, right); // 将基准放到正确位置
return i;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
关键逻辑:
- 分区:通过
pivot
将数组分为两部分,左边小于pivot
,右边大于等于。 - 时间复杂度:平均O(n log n),最坏O(n²)(可通过随机选择基准优化)。
📅 Day 3:递归与分治
🔥 题目:汉诺塔问题
考点:递归的终止条件与分治思想。
代码:
public class HanoiTower {
public static void move(int n, char from, char to, char temp) {
if (n == 1) {
System.out.println("Move disk 1 from " + from + " to " + to);
return;
}
// 步骤1:将n-1个盘子从from移动到temp
move(n - 1, from, temp, to);
// 步骤2:将第n个盘子从from移动到to
System.out.println("Move disk " + n + " from " + from + " to " + to);
// 步骤3:将n-1个盘子从temp移动到to
move(n - 1, temp, to, from);
}
}
关键逻辑:
- 递归三步走:
- 将n-1个盘子移动到辅助柱。
- 移动第n个盘子到目标柱。
- 将n-1个盘子从辅助柱移动到目标柱。
📅 Day 4:字符串与数组
🔥 题目:旋转数组的最小值
考点:二分查找的变体应用。
题目描述:输入一个旋转数组(如[3,4,5,1,2]
),输出最小值。
代码:
public class FindMinInRotateArray {
public int minArray(int[] numbers) {
int left = 0, right = numbers.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (numbers[mid] > numbers[right]) {
left = mid + 1; // 最小值在右半部分
} else if (numbers[mid] < numbers[right]) {
right = mid; // 缩小范围
} else {
right--; // 相等时无法判断,缩小右边界
}
}
return numbers[left];
}
}
关键逻辑:
- 二分查找变形:通过比较
mid
与right
的值判断最小值所在区间。 - 特殊情况处理:当
numbers[mid] == numbers[right]
时,无法确定,需逐步缩小右边界。
🔥 题目:最长回文子串(Manacher算法优化)
考点:中心扩展法与Manacher算法优化。
题目描述:输入字符串s
,输出最长回文子串。
代码(中心扩展法):
public String longestPalindrome(String s) {
if (s == null || s.length() < 2) return s;
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i); // 奇数长度
int len2 = expandAroundCenter(s, i, i + 1); // 偶数长度
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int L, int R) {
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--; R++;
}
return R - L - 1; // 返回回文长度
}
关键逻辑:
- 中心扩展法:遍历每个字符作为中心,向两边扩展,时间复杂度O(n²)。
- 优化方向:Manacher算法可将时间复杂度降至O(n),但代码复杂度较高,面试中优先使用中心扩展法。
📅 Day 5:动态规划
🔥 题目:最长公共子序列(LCS)
考点:DP表构建与状态转移方程。
代码:
public class LongestCommonSubsequence {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
}
关键逻辑:
- 状态转移方程:
- 若字符相等:
dp[i][j] = dp[i-1][j-1] + 1
- 否则:取左或上值的最大值。
- 若字符相等:
🔥 题目:蛇形打印二叉树(字节跳动高频题)
考点:层次遍历与双栈实现蛇形打印。
题目描述:按蛇形(ZigZag)顺序打印二叉树的每一层节点。
代码:
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean isLeftToRight = true; // 控制方向
while (!queue.isEmpty()) {
int size = queue.size();
LinkedList<Integer> level = new LinkedList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (isLeftToRight)
level.addLast(node.val); // 从左到右直接添加
else
level.addFirst(node.val); // 从右到左逆序添加
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
res.add(level);
isLeftToRight = !isLeftToRight; // 切换方向
}
return res;
}
关键逻辑:
- 层次遍历:使用队列逐层遍历。
- 方向控制:通过
isLeftToRight
标志位,偶数层用addFirst()
实现逆序。
📅 Day 6:树与图
🔥 题目:二叉树前序遍历
考点:递归与迭代实现方式。
代码(递归):
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
res.add(root.val); // 先访问根
res.addAll(preorderTraversal(root.left)); // 遍历左子树
res.addAll(preorderTraversal(root.right)); // 遍历右子树
return res;
}
代码(迭代):
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if (root != null) stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.add(node.val); // 先访问根
// 先压右节点,再压左节点(保证左先出栈)
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
return res;
}
🔥 题目:旋转链表(LeetCode 61,字节/腾讯高频题)
考点:虚拟头节点+双指针定位新头尾。
题目描述:将链表向右旋转k
次,返回旋转后的头节点。
代码:
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null || k == 0) return head;
int len = 0;
ListNode cur = head;
while (cur != null) {
len++;
cur = cur.next;
}
k %= len; // 处理k超过长度的情况
if (k == 0) return head;
// 虚拟头节点+双指针
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode fast = dummy, slow = dummy;
// fast先走k步
for (int i = 0; i < k; i++) {
fast = fast.next;
}
// 同时移动直到fast到尾部
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 重组链表
ListNode newHead = slow.next;
slow.next = null; // 断开原链表
fast.next = dummy.next; // 尾连原头
return newHead;
}
关键逻辑:
- 虚拟头节点:避免头节点处理的边界条件。
- 双指针定位:
fast
先走k
步后,slow
与fast
同步移动,最终slow
指向新尾节点前驱。
📅 Day 7:高频题实战与总结
🔥 题目:斐波那契数列(动态规划优化)
考点:递归的低效与动态规划的优化。
代码(动态规划):
public int fib(int n) {
if (n <= 1) return n;
int prev = 0, curr = 1;
for (int i = 2; i <= n; i++) {
int sum = prev + curr;
prev = curr;
curr = sum;
}
return curr;
}
优化点:
- 空间优化:从O(n) → O(1),仅用变量记录前两项。
🔥 题目:不同二叉搜索树(LeetCode 96,阿里/美团高频题)
考点:卡特兰数与动态规划状态转移。
题目描述:给定n
,求由1~n
可组成的不同BST数量。
代码:
public int numTrees(int n) {
int[] dp = new int[n + 1];
dp[0] = 1; // 空树也是一种情况
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
// 以j为根,左子树有j-1个节点,右子树有i-j个节点
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
关键逻辑:
- 状态转移方程:
dp[i] += dp[j-1] * dp[i-j]
,枚举每个j
作为根节点。 - 数学本质:卡特兰数公式
C(2n, n)/(n+1)
,但动态规划更易理解。
📌 实战案例总结
🔥 大厂高频题型扩展
-
合并区间(LeetCode 56):
public int[][] merge(int[][] intervals) { Arrays.sort(intervals, (a, b) -> a[0] - b[0]); List<int[]> res = new ArrayList<>(); for (int[] interval : intervals) { if (res.isEmpty() || res.get(res.size()-1)[1] < interval[0]) { res.add(interval); } else { res.get(res.size()-1)[1] = Math.max(res.get(res.size()-1)[1], interval[1]); } } return res.toArray(new int[res.size()][]); }
考点:排序+贪心合并。
-
两数之和(哈希表优化)(LeetCode 1):
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(1)
查询优化。
🌟 备考建议
-
高频题专项突破:重点刷LeetCode标签中的
Top 100 Liked
和`高频面试。 leetcode.cn/problem-lis… -
大厂真题实战:优先练习
腾讯
、阿里
、字节
等大厂的高频题(如旋转链表、蛇形打印)。 -
代码规范与细节:注意边界条件(如空链表、
k=0
),避免NullPointerException
。 -
模拟面试复盘:用白板写出代码逻辑,锻炼思维清晰度!
💪 5-7天坚持下来,算法面试必过!
关注公众号【爪哇喔】,获取更多面试资料!