5-7天Java算法速成计划 | 互联网大厂高频题全解析

96 阅读7分钟

🚀 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栈顶(即队列首元素)  
    }  
}  

关键逻辑

  1. push()直接操作stack1,时间复杂度O(1)
  2. 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;  
    }  
}  

关键逻辑

  1. 分区:通过pivot将数组分为两部分,左边小于pivot,右边大于等于。
  2. 时间复杂度:平均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);  
    }  
}  

关键逻辑

  1. 递归三步走
    • 将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];  
    }  
}  

关键逻辑

  1. 二分查找变形:通过比较midright的值判断最小值所在区间。
  2. 特殊情况处理:当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; // 返回回文长度  
}  

关键逻辑

  1. 中心扩展法:遍历每个字符作为中心,向两边扩展,时间复杂度O(n²)
  2. 优化方向: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];  
    }  
}  

关键逻辑

  1. 状态转移方程
    • 若字符相等: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;  
}  

关键逻辑

  1. 层次遍历:使用队列逐层遍历。
  2. 方向控制:通过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;  
}  

关键逻辑

  1. 虚拟头节点:避免头节点处理的边界条件。
  2. 双指针定位fast先走k步后,slowfast同步移动,最终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;  
}  

优化点

  1. 空间优化:从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];  
}  

关键逻辑

  1. 状态转移方程dp[i] += dp[j-1] * dp[i-j],枚举每个j作为根节点。
  2. 数学本质:卡特兰数公式C(2n, n)/(n+1),但动态规划更易理解。

📌 实战案例总结

🔥 大厂高频题型扩展

  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()][]);  
    }  
    

    考点:排序+贪心合并。

  2. 两数之和(哈希表优化)(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)查询优化。


🌟 备考建议

  1. 高频题专项突破:重点刷LeetCode标签中的Top 100 Liked和`高频面试。 leetcode.cn/problem-lis…

  2. 大厂真题实战:优先练习腾讯阿里字节等大厂的高频题(如旋转链表、蛇形打印)。

  3. 代码规范与细节:注意边界条件(如空链表、k=0),避免NullPointerException

  4. 模拟面试复盘:用白板写出代码逻辑,锻炼思维清晰度!

💪 5-7天坚持下来,算法面试必过!
关注公众号【爪哇喔】,获取更多面试资料!