01.09 Java基础篇|算法与数据结构实战

01.09 Java基础篇|算法与数据结构实战

导读

  • 目标:掌握常用算法和数据结构,能够分析时间复杂度和空间复杂度,解决实际编程问题。
  • 适用场景:算法面试、性能优化、代码重构、系统设计。

核心数据结构

1. 数组与链表

数组特点

  • 连续内存,随机访问 O(1)
  • 插入删除 O(n)
  • 适合查找和遍历

链表特点

  • 非连续内存,顺序访问
  • 插入删除 O(1)
  • 适合频繁插入删除

应用场景

java 复制代码
// 数组:适合固定大小、随机访问
int[] arr = new int[10];
arr[5] = 100;  // O(1)

// 链表:适合动态大小、频繁插入删除
LinkedList<Integer> list = new LinkedList<>();
list.addFirst(1);  // O(1)

2. 栈与队列

栈(LIFO)

java 复制代码
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.pop();  // 后进先出

队列(FIFO)

java 复制代码
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.poll();  // 先进先出

应用场景

  • 栈:表达式求值、括号匹配、函数调用
  • 队列:任务调度、消息队列、BFS遍历

3. 树与图

二叉树

java 复制代码
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
}

// 遍历方式
// 1. 前序遍历:根-左-右
// 2. 中序遍历:左-根-右
// 3. 后序遍历:左-右-根
// 4. 层序遍历:按层遍历

应用场景

  • 二叉搜索树:有序数据查找
  • 红黑树:HashMap底层实现
  • 堆:优先队列、TopK问题

常用算法

1. 排序算法

快速排序(Quick Sort)

实现

java 复制代码
public void quickSort(int[] arr, int left, int right) {
    if (left >= right) return;
    
    int pivot = partition(arr, left, right);
    quickSort(arr, left, pivot - 1);
    quickSort(arr, pivot + 1, right);
}

private 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 void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

优化版本(三路快排,处理重复元素)

java 复制代码
public void quickSort3Way(int[] arr, int left, int right) {
    if (left >= right) return;
    
    int pivot = arr[left];
    int lt = left;      // arr[left+1...lt] < pivot
    int gt = right + 1; // arr[gt...right] > pivot
    int i = left + 1;   // arr[lt+1...i) == pivot
    
    while (i < gt) {
        if (arr[i] < pivot) {
            swap(arr, i++, ++lt);
        } else if (arr[i] > pivot) {
            swap(arr, i, --gt);
        } else {
            i++;
        }
    }
    swap(arr, left, lt);
    
    quickSort3Way(arr, left, lt - 1);
    quickSort3Way(arr, gt, right);
}
归并排序(Merge Sort)

实现

java 复制代码
public void mergeSort(int[] arr, int left, int right) {
    if (left >= right) return;
    
    int mid = left + (right - left) / 2;
    mergeSort(arr, left, mid);
    mergeSort(arr, mid + 1, right);
    merge(arr, left, mid, right);
}

private void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[right - left + 1];
    int i = left, j = mid + 1, k = 0;
    
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    
    System.arraycopy(temp, 0, arr, left, temp.length);
}
堆排序(Heap Sort)

实现

java 复制代码
public void heapSort(int[] arr) {
    int n = arr.length;
    
    // 构建最大堆
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    
    // 逐个取出堆顶元素
    for (int i = n - 1; i > 0; i--) {
        swap(arr, 0, i);
        heapify(arr, i, 0);
    }
}

private void heapify(int[] arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    
    if (left < n && arr[left] > arr[largest]) {
        largest = left;
    }
    
    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }
    
    if (largest != i) {
        swap(arr, i, largest);
        heapify(arr, n, largest);
    }
}

时间复杂度对比

算法 平均 最坏 空间 稳定性 适用场景
快速排序 O(n log n) O(n²) O(log n) 不稳定 通用排序,平均性能最好
归并排序 O(n log n) O(n log n) O(n) 稳定 需要稳定排序,外部排序
堆排序 O(n log n) O(n log n) O(1) 不稳定 TopK问题,空间受限
冒泡排序 O(n²) O(n²) O(1) 稳定 教学示例,小规模数据
插入排序 O(n²) O(n²) O(1) 稳定 小规模数据,部分有序
计数排序 O(n + k) O(n + k) O(k) 稳定 数据范围小,整数排序

2. 查找算法

二分查找(Binary Search)

基础版本

java 复制代码
public int binarySearch(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;  // 防止溢出
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

查找左边界(第一个等于target的位置)

java 复制代码
public int binarySearchLeft(int[] arr, int target) {
    int left = 0, right = arr.length;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left < arr.length && arr[left] == target ? left : -1;
}

查找右边界(最后一个等于target的位置)

java 复制代码
public int binarySearchRight(int[] arr, int target) {
    int left = 0, right = arr.length;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] <= target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left > 0 && arr[left - 1] == target ? left - 1 : -1;
}

旋转数组查找

java 复制代码
public int searchRotatedArray(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[left] <= nums[mid]) {
            if (nums[left] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        } else {  // 右半部分有序
            if (nums[mid] < target && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

时间复杂度:O(log n)

应用场景

  • 有序数组查找
  • 查找插入位置
  • 查找范围
  • 旋转数组查找

3. 双指针算法

核心思想:使用两个指针在数组或链表中协同工作,减少遍历次数。

快慢指针

链表环检测

java 复制代码
public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) return false;
    
    ListNode slow = head;
    ListNode fast = head.next;
    
    while (fast != null && fast.next != null) {
        if (slow == fast) return true;
        slow = slow.next;
        fast = fast.next.next;
    }
    return false;
}

链表中点

java 复制代码
public ListNode findMiddle(ListNode head) {
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}
左右指针

两数之和(有序数组)

java 复制代码
public int[] twoSum(int[] numbers, int target) {
    int left = 0, right = numbers.length - 1;
    
    while (left < right) {
        int sum = numbers[left] + numbers[right];
        if (sum == target) {
            return new int[]{left + 1, right + 1};
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return new int[]{-1, -1};
}

三数之和

java 复制代码
public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);
    
    for (int i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue;  // 去重
        
        int left = i + 1, right = nums.length - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                while (left < right && nums[left] == nums[left + 1]) left++;
                while (left < right && nums[right] == nums[right - 1]) right--;
                left++;
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                right--;
            }
        }
    }
    return result;
}

盛水最多的容器

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;
}

4. 滑动窗口算法

核心思想:维护一个窗口,通过移动窗口边界来解决问题。

固定窗口大小

大小为K的子数组最大和

java 复制代码
public int maxSumSubarray(int[] nums, int k) {
    int windowSum = 0;
    for (int i = 0; i < k; i++) {
        windowSum += nums[i];
    }
    
    int maxSum = windowSum;
    for (int i = k; i < nums.length; i++) {
        windowSum = windowSum - nums[i - k] + nums[i];
        maxSum = Math.max(maxSum, windowSum);
    }
    return maxSum;
}
可变窗口大小

无重复字符的最长子串

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)) {
            left = Math.max(left, map.get(c) + 1);
        }
        map.put(c, right);
        maxLen = Math.max(maxLen, right - left + 1);
    }
    return maxLen;
}

最小覆盖子串

java 复制代码
public String minWindow(String s, String t) {
    Map<Character, Integer> need = new HashMap<>();
    Map<Character, Integer> window = new HashMap<>();
    
    for (char c : t.toCharArray()) {
        need.put(c, need.getOrDefault(c, 0) + 1);
    }
    
    int left = 0, right = 0;
    int valid = 0;
    int start = 0, len = Integer.MAX_VALUE;
    
    while (right < s.length()) {
        char c = s.charAt(right);
        right++;
        
        if (need.containsKey(c)) {
            window.put(c, window.getOrDefault(c, 0) + 1);
            if (window.get(c).equals(need.get(c))) {
                valid++;
            }
        }
        
        while (valid == need.size()) {
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            
            char d = s.charAt(left);
            left++;
            
            if (need.containsKey(d)) {
                if (window.get(d).equals(need.get(d))) {
                    valid--;
                }
                window.put(d, window.get(d) - 1);
            }
        }
    }
    
    return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}

应用场景

  • 子串/子数组问题
  • 字符串匹配
  • 连续子数组问题
  • 固定窗口统计

5. 动态规划(Dynamic Programming)

核心思想:将问题分解为子问题,通过记忆化避免重复计算。

基础DP

斐波那契数列

java 复制代码
// 递归(低效,O(2^n))
public int fib(int n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}

// 动态规划(高效,O(n))
public int fibDP(int n) {
    if (n <= 1) return n;
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

// 空间优化(O(1))
public int fibOptimized(int n) {
    if (n <= 1) return n;
    int prev2 = 0, prev1 = 1;
    for (int i = 2; i <= n; i++) {
        int curr = prev1 + prev2;
        prev2 = prev1;
        prev1 = curr;
    }
    return prev1;
}
经典DP问题

最长公共子序列(LCS)

java 复制代码
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];
}

0-1背包问题

java 复制代码
public int knapsack(int[] weights, int[] values, int capacity) {
    int n = weights.length;
    int[][] dp = new int[n + 1][capacity + 1];
    
    for (int i = 1; i <= n; i++) {
        for (int w = 1; w <= capacity; w++) {
            if (weights[i - 1] > w) {
                dp[i][w] = dp[i - 1][w];
            } else {
                dp[i][w] = Math.max(
                    dp[i - 1][w],
                    dp[i - 1][w - weights[i - 1]] + values[i - 1]
                );
            }
        }
    }
    return dp[n][capacity];
}

// 空间优化版本
public int knapsackOptimized(int[] weights, int[] values, int capacity) {
    int[] dp = new int[capacity + 1];
    
    for (int i = 0; i < weights.length; i++) {
        for (int w = capacity; w >= weights[i]; w--) {
            dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
        }
    }
    return dp[capacity];
}

股票买卖问题(最多一次交易)

java 复制代码
public int maxProfit(int[] prices) {
    int minPrice = Integer.MAX_VALUE;
    int maxProfit = 0;
    
    for (int price : prices) {
        minPrice = Math.min(minPrice, price);
        maxProfit = Math.max(maxProfit, price - minPrice);
    }
    return maxProfit;
}

股票买卖问题(无限次交易)

java 复制代码
public int maxProfitUnlimited(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;
}

最长递增子序列(LIS)

java 复制代码
public int lengthOfLIS(int[] nums) {
    int[] dp = new int[nums.length];
    Arrays.fill(dp, 1);
    int maxLen = 1;
    
    for (int i = 1; i < nums.length; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[j] < nums[i]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        maxLen = Math.max(maxLen, dp[i]);
    }
    return maxLen;
}

// 二分优化版本 O(n log n)
public int lengthOfLISOptimized(int[] nums) {
    List<Integer> tails = new ArrayList<>();
    
    for (int num : nums) {
        int pos = Collections.binarySearch(tails, num);
        if (pos < 0) pos = -(pos + 1);
        
        if (pos == tails.size()) {
            tails.add(num);
        } else {
            tails.set(pos, num);
        }
    }
    return tails.size();
}

DP解题步骤

  1. 定义状态:dp[i] 或 dp[i][j] 表示什么
  2. 状态转移方程:如何从子问题推导当前问题
  3. 初始状态:边界条件
  4. 计算顺序:确保子问题先计算
  5. 空间优化:如果可能,减少空间复杂度

应用场景

  • 最优化问题
  • 计数问题
  • 存在性问题
  • 序列问题

6. 贪心算法(Greedy)

核心思想:每一步都做出当前看起来最优的选择,希望最终得到全局最优解。

贪心算法适用条件

  1. 最优子结构:问题的最优解包含子问题的最优解
  2. 贪心选择性质:可以通过局部最优选择达到全局最优

经典问题

活动选择问题
java 复制代码
// 选择最多的不重叠活动
public int maxActivities(int[][] intervals) {
    if (intervals.length == 0) return 0;
    
    // 按结束时间排序
    Arrays.sort(intervals, (a, b) -> a[1] - b[1]);
    
    int count = 1;
    int end = intervals[0][1];
    
    for (int i = 1; i < intervals.length; i++) {
        if (intervals[i][0] >= end) {
            count++;
            end = intervals[i][1];
        }
    }
    return count;
}
跳跃游戏
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 true;
}

// 最少跳跃次数
public int jump(int[] nums) {
    int jumps = 0, farthest = 0, end = 0;
    
    for (int i = 0; i < nums.length - 1; i++) {
        farthest = Math.max(farthest, i + nums[i]);
        if (i == end) {
            jumps++;
            end = farthest;
        }
    }
    return jumps;
}
分发糖果
java 复制代码
public int candy(int[] ratings) {
    int n = ratings.length;
    int[] candies = new int[n];
    Arrays.fill(candies, 1);
    
    // 从左到右:如果右边评分更高,右边糖果数+1
    for (int i = 1; i < n; i++) {
        if (ratings[i] > ratings[i - 1]) {
            candies[i] = candies[i - 1] + 1;
        }
    }
    
    // 从右到左:如果左边评分更高,左边糖果数+1
    for (int i = n - 2; i >= 0; i--) {
        if (ratings[i] > ratings[i + 1]) {
            candies[i] = Math.max(candies[i], candies[i + 1] + 1);
        }
    }
    
    return Arrays.stream(candies).sum();
}
区间调度问题
java 复制代码
// 用最少的箭射爆所有气球
public int findMinArrowShots(int[][] points) {
    if (points.length == 0) return 0;
    
    Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));
    
    int arrows = 1;
    int end = points[0][1];
    
    for (int i = 1; i < points.length; i++) {
        if (points[i][0] > end) {
            arrows++;
            end = points[i][1];
        }
    }
    return arrows;
}

贪心 vs 动态规划

特性 贪心算法 动态规划
选择方式 局部最优 全局最优
计算方式 自顶向下 自底向上
适用条件 贪心选择性质 最优子结构
时间复杂度 通常较低 可能较高
正确性 需要证明 保证正确

7. 回溯算法(Backtracking)

核心思想:通过尝试所有可能的路径来找到解,如果当前路径不可行,则回退到上一步。

回溯算法模板

java 复制代码
public void backtrack(参数列表) {
    // 终止条件
    if (终止条件) {
        保存结果;
        return;
    }
    
    // 遍历所有可能的选择
    for (选择 : 所有可能的选择) {
        // 做选择
        做出选择;
        
        // 递归
        backtrack(新参数);
        
        // 撤销选择(回溯)
        撤销选择;
    }
}

经典问题

全排列
java 复制代码
public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] used = new boolean[nums.length];
    
    backtrack(nums, used, path, result);
    return result;
}

private void backtrack(int[] nums, boolean[] used, 
                      List<Integer> path, List<List<Integer>> result) {
    // 终止条件
    if (path.size() == nums.length) {
        result.add(new ArrayList<>(path));
        return;
    }
    
    // 遍历所有可能的选择
    for (int i = 0; i < nums.length; i++) {
        if (used[i]) continue;
        
        // 做选择
        path.add(nums[i]);
        used[i] = true;
        
        // 递归
        backtrack(nums, used, path, result);
        
        // 撤销选择
        path.remove(path.size() - 1);
        used[i] = false;
    }
}
N皇后问题
java 复制代码
public List<List<String>> solveNQueens(int n) {
    List<List<String>> result = new ArrayList<>();
    char[][] board = new char[n][n];
    for (char[] row : board) {
        Arrays.fill(row, '.');
    }
    
    backtrack(board, 0, result);
    return result;
}

private void backtrack(char[][] board, int row, List<List<String>> result) {
    if (row == board.length) {
        result.add(construct(board));
        return;
    }
    
    for (int col = 0; col < board.length; col++) {
        if (!isValid(board, row, col)) continue;
        
        board[row][col] = 'Q';
        backtrack(board, row + 1, result);
        board[row][col] = '.';
    }
}

private boolean isValid(char[][] board, int row, int col) {
    // 检查列
    for (int i = 0; i < row; i++) {
        if (board[i][col] == 'Q') return false;
    }
    
    // 检查左上对角线
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q') return false;
    }
    
    // 检查右上对角线
    for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) {
        if (board[i][j] == 'Q') return false;
    }
    
    return true;
}
组合总和
java 复制代码
public List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    
    Arrays.sort(candidates);
    backtrack(candidates, target, 0, path, result);
    return result;
}

private void backtrack(int[] candidates, int target, int start,
                      List<Integer> path, List<List<Integer>> result) {
    if (target == 0) {
        result.add(new ArrayList<>(path));
        return;
    }
    
    if (target < 0) return;
    
    for (int i = start; i < candidates.length; i++) {
        path.add(candidates[i]);
        backtrack(candidates, target - candidates[i], i, path, result);
        path.remove(path.size() - 1);
    }
}
单词搜索
java 复制代码
public boolean exist(char[][] board, String word) {
    int m = board.length, n = board[0].length;
    boolean[][] visited = new boolean[m][n];
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (backtrack(board, word, 0, i, j, visited)) {
                return true;
            }
        }
    }
    return false;
}

private boolean backtrack(char[][] board, String word, int index,
                         int row, int col, boolean[][] visited) {
    if (index == word.length()) return true;
    
    if (row < 0 || row >= board.length || col < 0 || col >= board[0].length ||
        visited[row][col] || board[row][col] != word.charAt(index)) {
        return false;
    }
    
    visited[row][col] = true;
    
    boolean found = backtrack(board, word, index + 1, row + 1, col, visited) ||
                    backtrack(board, word, index + 1, row - 1, col, visited) ||
                    backtrack(board, word, index + 1, row, col + 1, visited) ||
                    backtrack(board, word, index + 1, row, col - 1, visited);
    
    visited[row][col] = false;
    return found;
}

应用场景

  • 排列组合问题
  • 棋盘类问题(N皇后、数独)
  • 路径搜索问题
  • 约束满足问题

应用系统常用算法

1. 哈希算法

一致性哈希(Consistent Hashing)

应用场景:分布式缓存、负载均衡

实现

java 复制代码
import java.util.*;

public class ConsistentHash {
    // 虚拟节点数
    private static final int VIRTUAL_NODES = 150;
    // 哈希环:TreeMap保证有序
    private final TreeMap<Long, String> hashRing = new TreeMap<>();
    
    public ConsistentHash(List<String> servers) {
        for (String server : servers) {
            // 为每个物理节点创建虚拟节点
            for (int i = 0; i < VIRTUAL_NODES; i++) {
                String virtualNode = server + "#" + i;
                long hash = hash(virtualNode);
                hashRing.put(hash, server);
            }
        }
    }
    
    // 根据key获取服务器
    public String getServer(String key) {
        if (hashRing.isEmpty()) return null;
        
        long hash = hash(key);
        // 找到第一个大于等于hash的节点
        Map.Entry<Long, String> entry = hashRing.ceilingEntry(hash);
        
        if (entry == null) {
            // 如果没有,返回第一个节点(环形)
            entry = hashRing.firstEntry();
        }
        
        return entry.getValue();
    }
    
    // 添加服务器
    public void addServer(String server) {
        for (int i = 0; i < VIRTUAL_NODES; i++) {
            String virtualNode = server + "#" + i;
            long hash = hash(virtualNode);
            hashRing.put(hash, server);
        }
    }
    
    // 移除服务器
    public void removeServer(String server) {
        for (int i = 0; i < VIRTUAL_NODES; i++) {
            String virtualNode = server + "#" + i;
            long hash = hash(virtualNode);
            hashRing.remove(hash);
        }
    }
    
    // FNV-1a哈希算法
    private long hash(String key) {
        final int p = 16777619;
        long hash = 2166136261L;
        for (int i = 0; i < key.length(); i++) {
            hash = (hash ^ key.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        return hash < 0 ? Math.abs(hash) : hash;
    }
}
布隆过滤器(Bloom Filter)

应用场景:缓存穿透防护、URL去重、垃圾邮件过滤

实现

java 复制代码
import java.util.BitSet;

public class BloomFilter {
    private BitSet bitSet;
    private int bitSetSize;
    private int numHashFunctions;
    private int numAdded;
    
    public BloomFilter(int expectedInsertions, double falsePositiveRate) {
        // 计算位数组大小:m = -n * ln(p) / (ln(2)^2)
        this.bitSetSize = (int) (-expectedInsertions * Math.log(falsePositiveRate) / 
                                  (Math.log(2) * Math.log(2)));
        // 计算哈希函数数量:k = (m/n) * ln(2)
        this.numHashFunctions = (int) (bitSetSize / expectedInsertions * Math.log(2));
        this.bitSet = new BitSet(bitSetSize);
        this.numAdded = 0;
    }
    
    // 添加元素
    public void add(String item) {
        for (int i = 0; i < numHashFunctions; i++) {
            int hash = hash(item, i);
            bitSet.set(hash % bitSetSize, true);
        }
        numAdded++;
    }
    
    // 检查元素是否存在(可能误判,但不会漏判)
    public boolean mightContain(String item) {
        for (int i = 0; i < numHashFunctions; i++) {
            int hash = hash(item, i);
            if (!bitSet.get(hash % bitSetSize)) {
                return false;  // 肯定不存在
            }
        }
        return true;  // 可能存在
    }
    
    private int hash(String item, int seed) {
        int hash = 0;
        for (char c : item.toCharArray()) {
            hash = hash * seed + c;
        }
        return Math.abs(hash);
    }
}

2. 限流算法

令牌桶算法(Token Bucket)

应用场景:API限流、流量控制

实现

java 复制代码
import java.util.concurrent.atomic.AtomicLong;

public class TokenBucket {
    private final long capacity;        // 桶容量
    private final long refillRate;      // 每秒补充令牌数
    private AtomicLong tokens;          // 当前令牌数
    private long lastRefillTime;       // 上次补充时间
    
    public TokenBucket(long capacity, long refillRate) {
        this.capacity = capacity;
        this.refillRate = refillRate;
        this.tokens = new AtomicLong(capacity);
        this.lastRefillTime = System.currentTimeMillis();
    }
    
    // 尝试获取令牌
    public boolean tryAcquire(int permits) {
        refill();
        long currentTokens = tokens.get();
        if (currentTokens >= permits) {
            return tokens.compareAndSet(currentTokens, currentTokens - permits);
        }
        return false;
    }
    
    // 补充令牌
    private synchronized void refill() {
        long now = System.currentTimeMillis();
        long elapsed = now - lastRefillTime;
        
        if (elapsed > 0) {
            // 计算需要补充的令牌数
            long tokensToAdd = (elapsed * refillRate) / 1000;
            if (tokensToAdd > 0) {
                long currentTokens = tokens.get();
                long newTokens = Math.min(capacity, currentTokens + tokensToAdd);
                tokens.set(newTokens);
                lastRefillTime = now;
            }
        }
    }
}
滑动窗口限流

实现

java 复制代码
import java.util.concurrent.ConcurrentLinkedQueue;

public class SlidingWindowRateLimiter {
    private final int maxRequests;      // 窗口内最大请求数
    private final long windowSizeMs;   // 窗口大小(毫秒)
    private final ConcurrentLinkedQueue<Long> requests;  // 请求时间戳队列
    
    public SlidingWindowRateLimiter(int maxRequests, long windowSizeMs) {
        this.maxRequests = maxRequests;
        this.windowSizeMs = windowSizeMs;
        this.requests = new ConcurrentLinkedQueue<>();
    }
    
    public boolean allow() {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSizeMs;
        
        // 移除窗口外的请求
        while (!requests.isEmpty() && requests.peek() < windowStart) {
            requests.poll();
        }
        
        // 检查是否超过限制
        if (requests.size() < maxRequests) {
            requests.offer(now);
            return true;
        }
        
        return false;
    }
}

3. LRU缓存算法

应用场景:缓存系统、页面置换

实现

java 复制代码
import java.util.*;

public class LRUCache<K, V> {
    class Node {
        K key;
        V value;
        Node prev;
        Node next;
        
        Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }
    
    private final int capacity;
    private final Map<K, Node> cache;
    private final Node head;  // 虚拟头节点
    private final Node tail;  // 虚拟尾节点
    
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>();
        this.head = new Node(null, null);
        this.tail = new Node(null, null);
        head.next = tail;
        tail.prev = head;
    }
    
    public V get(K key) {
        Node node = cache.get(key);
        if (node == null) return null;
        
        // 移动到头部
        moveToHead(node);
        return node.value;
    }
    
    public void put(K key, V value) {
        Node node = cache.get(key);
        
        if (node != null) {
            // 更新值并移动到头部
            node.value = value;
            moveToHead(node);
        } else {
            // 新建节点
            node = new Node(key, value);
            cache.put(key, node);
            addToHead(node);
            
            // 如果超过容量,删除尾部节点
            if (cache.size() > capacity) {
                Node last = removeTail();
                cache.remove(last.key);
            }
        }
    }
    
    private void addToHead(Node node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }
    
    private void removeNode(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    
    private void moveToHead(Node node) {
        removeNode(node);
        addToHead(node);
    }
    
    private Node removeTail() {
        Node last = tail.prev;
        removeNode(last);
        return last;
    }
}

4. TopK算法

应用场景:热门商品、热搜词、排行榜

实现

java 复制代码
// 方法1:堆排序 O(n log k)
public int[] topKByHeap(int[] nums, int k) {
    PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    
    for (int num : nums) {
        if (minHeap.size() < k) {
            minHeap.offer(num);
        } else if (num > minHeap.peek()) {
            minHeap.poll();
            minHeap.offer(num);
        }
    }
    
    int[] result = new int[k];
    for (int i = k - 1; i >= 0; i--) {
        result[i] = minHeap.poll();
    }
    return result;
}

// 方法2:快速选择 O(n) 平均时间复杂度
public int[] topKByQuickSelect(int[] nums, int k) {
    quickSelect(nums, 0, nums.length - 1, k);
    int[] result = new int[k];
    System.arraycopy(nums, nums.length - k, result, 0, k);
    Arrays.sort(result);
    return result;
}

private void quickSelect(int[] nums, int left, int right, int k) {
    if (left >= right) return;
    
    int pivot = partition(nums, left, right);
    int rightSize = right - pivot + 1;
    
    if (rightSize == k) return;
    else if (rightSize > k) {
        quickSelect(nums, pivot + 1, right, k);
    } else {
        quickSelect(nums, left, pivot - 1, k - rightSize);
    }
}

5. 并查集(Union-Find)

应用场景:朋友圈、连通性问题、最小生成树

实现

java 复制代码
public class UnionFind {
    private int[] parent;
    private int[] rank;  // 用于路径压缩优化
    
    public UnionFind(int n) {
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
    }
    
    // 查找根节点(路径压缩)
    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);  // 路径压缩
        }
        return parent[x];
    }
    
    // 合并两个集合(按秩合并)
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        
        if (rootX == rootY) return;
        
        // 按秩合并:将秩小的树合并到秩大的树下
        if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
    }
    
    // 判断是否连通
    public boolean connected(int x, int y) {
        return find(x) == find(y);
    }
}

6. 字符串匹配算法

KMP算法

实现

java 复制代码
public class KMP {
    // 查找pattern在text中的位置
    public int search(String text, String pattern) {
        int[] next = buildNext(pattern);
        int i = 0, j = 0;
        
        while (i < text.length() && j < pattern.length()) {
            if (j == -1 || text.charAt(i) == pattern.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        
        return j == pattern.length() ? i - j : -1;
    }
    
    // 构建next数组(部分匹配表)
    private int[] buildNext(String pattern) {
        int[] next = new int[pattern.length()];
        next[0] = -1;
        int i = 0, j = -1;
        
        while (i < pattern.length() - 1) {
            if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) {
                i++;
                j++;
                next[i] = j;
            } else {
                j = next[j];
            }
        }
        return next;
    }
}

7. 分布式ID生成算法(雪花算法)

应用场景:分布式系统唯一ID生成

实现

java 复制代码
public class SnowflakeIdGenerator {
    // 时间戳起始点(2020-01-01)
    private static final long EPOCH = 1577836800000L;
    
    // 各部分占用位数
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;
    
    // 最大值
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
    
    // 位移
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
    
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("workerId must be between 0 and " + MAX_WORKER_ID);
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId must be between 0 and " + MAX_DATACENTER_ID);
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards");
        }
        
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        
        lastTimestamp = timestamp;
        
        return ((timestamp - EPOCH) << TIMESTAMP_SHIFT) |
               (datacenterId << DATACENTER_ID_SHIFT) |
               (workerId << WORKER_ID_SHIFT) |
               sequence;
    }
    
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

8. 负载均衡算法

加权轮询(Weighted Round Robin)

实现

java 复制代码
import java.util.*;

public class WeightedRoundRobin {
    static class Server {
        String name;
        int weight;
        int currentWeight;
        
        Server(String name, int weight) {
            this.name = name;
            this.weight = weight;
            this.currentWeight = 0;
        }
    }
    
    private List<Server> servers;
    
    public WeightedRoundRobin(List<Server> servers) {
        this.servers = new ArrayList<>(servers);
    }
    
    public Server select() {
        Server selected = null;
        int totalWeight = 0;
        
        for (Server server : servers) {
            server.currentWeight += server.weight;
            totalWeight += server.weight;
            
            if (selected == null || server.currentWeight > selected.currentWeight) {
                selected = server;
            }
        }
        
        if (selected != null) {
            selected.currentWeight -= totalWeight;
        }
        
        return selected;
    }
}

游戏主程常用算法

1. A*寻路算法

应用场景:游戏AI寻路、路径规划

实现

java 复制代码
import java.util.*;

public class AStar {
    static class Node {
        int x, y;
        int g;  // 从起点到当前节点的实际代价
        int h;  // 从当前节点到终点的启发式估计代价
        int f;  // f = g + h
        Node parent;
        
        Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
        
        int getF() {
            return g + h;
        }
    }
    
    public List<Node> findPath(int[][] grid, Node start, Node end) {
        PriorityQueue<Node> openList = new PriorityQueue<>(
            Comparator.comparingInt(Node::getF)
        );
        Set<String> closedSet = new HashSet<>();
        Map<String, Node> openMap = new HashMap<>();
        
        start.g = 0;
        start.h = heuristic(start, end);
        openList.offer(start);
        openMap.put(key(start), start);
        
        int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        
        while (!openList.isEmpty()) {
            Node current = openList.poll();
            openMap.remove(key(current));
            closedSet.add(key(current));
            
            if (current.x == end.x && current.y == end.y) {
                return reconstructPath(current);
            }
            
            for (int[] dir : directions) {
                int newX = current.x + dir[0];
                int newY = current.y + dir[1];
                
                if (newX < 0 || newX >= grid.length || 
                    newY < 0 || newY >= grid[0].length ||
                    grid[newX][newY] == 1) {  // 1表示障碍物
                    continue;
                }
                
                String neighborKey = key(newX, newY);
                if (closedSet.contains(neighborKey)) continue;
                
                Node neighbor = new Node(newX, newY);
                int tentativeG = current.g + 1;
                
                Node existing = openMap.get(neighborKey);
                if (existing == null || tentativeG < existing.g) {
                    neighbor.g = tentativeG;
                    neighbor.h = heuristic(neighbor, end);
                    neighbor.parent = current;
                    
                    if (existing == null) {
                        openList.offer(neighbor);
                        openMap.put(neighborKey, neighbor);
                    }
                }
            }
        }
        
        return new ArrayList<>();  // 无路径
    }
    
    // 曼哈顿距离作为启发式函数
    private int heuristic(Node a, Node b) {
        return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
    }
    
    private List<Node> reconstructPath(Node node) {
        List<Node> path = new ArrayList<>();
        while (node != null) {
            path.add(node);
            node = node.parent;
        }
        Collections.reverse(path);
        return path;
    }
    
    private String key(Node node) {
        return key(node.x, node.y);
    }
    
    private String key(int x, int y) {
        return x + "," + y;
    }
}

2. 碰撞检测算法

AABB(轴对齐包围盒)

实现

java 复制代码
public class AABB {
    float minX, minY, maxX, maxY;
    
    public boolean intersects(AABB other) {
        return !(this.maxX < other.minX || this.minX > other.maxX ||
                 this.maxY < other.minY || this.minY > other.maxY);
    }
    
    public boolean contains(float x, float y) {
        return x >= minX && x <= maxX && y >= minY && y <= maxY;
    }
}

3. 对象池算法

应用场景:频繁创建销毁的对象(子弹、粒子、特效)

实现

java 复制代码
import java.util.*;
import java.util.function.Supplier;
import java.util.function.Consumer;

public class ObjectPool<T> {
    private final Queue<T> pool;
    private final Supplier<T> factory;
    private final Consumer<T> reset;
    private final int maxSize;
    
    public ObjectPool(Supplier<T> factory, Consumer<T> reset, int maxSize) {
        this.factory = factory;
        this.reset = reset;
        this.maxSize = maxSize;
        this.pool = new LinkedList<>();
    }
    
    // 获取对象
    public T acquire() {
        T obj = pool.poll();
        if (obj == null) {
            obj = factory.get();
        }
        return obj;
    }
    
    // 归还对象
    public void release(T obj) {
        if (pool.size() < maxSize) {
            reset.accept(obj);
            pool.offer(obj);
        }
    }
}

4. 平滑插值算法

实现

java 复制代码
public class Interpolation {
    // 线性插值
    public static float lerp(float a, float b, float t) {
        return a + (b - a) * t;
    }
    
    // 平滑插值(缓入缓出)
    public static float smoothStep(float a, float b, float t) {
        t = t * t * (3.0f - 2.0f * t);  // smoothstep函数
        return lerp(a, b, t);
    }
    
    // 球面线性插值(用于旋转)
    public static float slerp(float a, float b, float t) {
        // 简化版本,实际需要处理角度环绕
        return lerp(a, b, t);
    }
}

5. 四叉树(Quadtree)

应用场景:空间分区、碰撞检测优化、LOD(细节层次)

实现

java 复制代码
import java.util.*;

public class Quadtree {
    static class Rectangle {
        float x, y, width, height;
        
        Rectangle(float x, float y, float width, float height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
        
        boolean contains(Point point) {
            return point.x >= x && point.x < x + width &&
                   point.y >= y && point.y < y + height;
        }
        
        boolean intersects(Rectangle other) {
            return !(x + width < other.x || other.x + other.width < x ||
                     y + height < other.y || other.y + other.height < y);
        }
    }
    
    static class Point {
        float x, y;
        Object data;
        
        Point(float x, float y, Object data) {
            this.x = x;
            this.y = y;
            this.data = data;
        }
    }
    
    private static final int MAX_OBJECTS = 4;
    private static final int MAX_LEVELS = 5;
    
    private int level;
    private List<Point> objects;
    private Rectangle bounds;
    private Quadtree[] nodes;
    
    public Quadtree(int level, Rectangle bounds) {
        this.level = level;
        this.bounds = bounds;
        this.objects = new ArrayList<>();
        this.nodes = new Quadtree[4];
    }
    
    // 插入点
    public void insert(Point point) {
        if (!bounds.contains(point)) return;
        
        if (objects.size() < MAX_OBJECTS || level >= MAX_LEVELS) {
            objects.add(point);
            return;
        }
        
        if (nodes[0] == null) {
            split();
        }
        
        for (Quadtree node : nodes) {
            node.insert(point);
        }
    }
    
    // 查询范围内的点
    public List<Point> query(Rectangle range) {
        List<Point> results = new ArrayList<>();
        
        if (!bounds.intersects(range)) {
            return results;
        }
        
        for (Point point : objects) {
            if (range.contains(point)) {
                results.add(point);
            }
        }
        
        if (nodes[0] != null) {
            for (Quadtree node : nodes) {
                results.addAll(node.query(range));
            }
        }
        
        return results;
    }
    
    // 分割四叉树
    private void split() {
        float subWidth = bounds.width / 2;
        float subHeight = bounds.height / 2;
        float x = bounds.x;
        float y = bounds.y;
        
        nodes[0] = new Quadtree(level + 1, 
            new Rectangle(x + subWidth, y, subWidth, subHeight));
        nodes[1] = new Quadtree(level + 1, 
            new Rectangle(x, y, subWidth, subHeight));
        nodes[2] = new Quadtree(level + 1, 
            new Rectangle(x, y + subHeight, subWidth, subHeight));
        nodes[3] = new Quadtree(level + 1, 
            new Rectangle(x + subWidth, y + subHeight, subWidth, subHeight));
    }
}

6. 伪随机数生成器

应用场景:游戏随机事件、地图生成、掉落系统

实现

java 复制代码
// 线性同余生成器(LCG)
public class PRNG {
    private long seed;
    private static final long MULTIPLIER = 1103515245L;
    private static final long INCREMENT = 12345L;
    private static final long MODULUS = (1L << 31);
    
    public PRNG(long seed) {
        this.seed = seed;
    }
    
    // 生成0到1之间的随机数
    public double nextDouble() {
        seed = (MULTIPLIER * seed + INCREMENT) % MODULUS;
        return (double) seed / MODULUS;
    }
    
    // 生成指定范围的随机整数
    public int nextInt(int min, int max) {
        return min + (int) (nextDouble() * (max - min));
    }
    
    // 设置种子(用于可重现的随机序列)
    public void setSeed(long seed) {
        this.seed = seed;
    }
}

7. 地图生成算法(Perlin噪声简化版)

应用场景:程序化生成地形、纹理

实现

java 复制代码
public class PerlinNoise {
    private static final int[] permutation = {
        151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
        // ... 更多随机排列
    };
    
    private static final int[] p = new int[512];
    
    static {
        for (int i = 0; i < 256; i++) {
            p[256 + i] = p[i] = permutation[i % permutation.length];
        }
    }
    
    // 简化版Perlin噪声
    public static double noise(double x, double y) {
        int X = (int) Math.floor(x) & 255;
        int Y = (int) Math.floor(y) & 255;
        
        double fx = x - Math.floor(x);
        double fy = y - Math.floor(y);
        
        double u = fade(fx);
        double v = fade(fy);
        
        int a = p[X] + Y;
        int b = p[X + 1] + Y;
        
        return lerp(v,
            lerp(u, grad(p[a], fx, fy), grad(p[b], fx - 1, fy)),
            lerp(u, grad(p[a + 1], fx, fy - 1), grad(p[b + 1], fx - 1, fy - 1))
        );
    }
    
    private static double fade(double t) {
        return t * t * t * (t * (t * 6 - 15) + 10);
    }
    
    private static double lerp(double t, double a, double b) {
        return a + t * (b - a);
    }
    
    private static double grad(int hash, double x, double y) {
        int h = hash & 3;
        return ((h & 1) == 0 ? x : -x) + ((h & 2) == 0 ? y : -y);
    }
}

面试高频算法题

1. 链表相关

反转链表
java 复制代码
// 迭代方式
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

// 递归方式
public ListNode reverseListRecursive(ListNode head) {
    if (head == null || head.next == null) return head;
    
    ListNode newHead = reverseListRecursive(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
}
合并K个有序链表
java 复制代码
public ListNode mergeKLists(ListNode[] lists) {
    if (lists == null || lists.length == 0) return null;
    
    PriorityQueue<ListNode> pq = new PriorityQueue<>(
        Comparator.comparingInt(a -> a.val)
    );
    
    for (ListNode node : lists) {
        if (node != null) pq.offer(node);
    }
    
    ListNode dummy = new ListNode(0);
    ListNode curr = dummy;
    
    while (!pq.isEmpty()) {
        ListNode node = pq.poll();
        curr.next = node;
        curr = curr.next;
        if (node.next != null) {
            pq.offer(node.next);
        }
    }
    
    return dummy.next;
}
链表相交
java 复制代码
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode pA = headA, pB = headB;
    
    while (pA != pB) {
        pA = pA == null ? headB : pA.next;
        pB = pB == null ? headA : pB.next;
    }
    
    return pA;
}

2. 二叉树相关

二叉树最大深度
java 复制代码
public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
验证二叉搜索树
java 复制代码
public boolean isValidBST(TreeNode root) {
    return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

private boolean validate(TreeNode node, long min, long max) {
    if (node == null) return true;
    if (node.val <= min || node.val >= max) return false;
    return validate(node.left, min, node.val) && 
           validate(node.right, node.val, max);
}
路径总和
java 复制代码
public boolean hasPathSum(TreeNode root, int targetSum) {
    if (root == null) return false;
    if (root.left == null && root.right == null) {
        return root.val == targetSum;
    }
    return hasPathSum(root.left, targetSum - root.val) ||
           hasPathSum(root.right, targetSum - root.val);
}

3. 数组/字符串相关

接雨水
java 复制代码
public int trap(int[] height) {
    int left = 0, right = height.length - 1;
    int leftMax = 0, rightMax = 0;
    int water = 0;
    
    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= leftMax) {
                leftMax = height[left];
            } else {
                water += leftMax - height[left];
            }
            left++;
        } else {
            if (height[right] >= rightMax) {
                rightMax = height[right];
            } else {
                water += rightMax - height[right];
            }
            right--;
        }
    }
    return water;
}
最长回文子串
java 复制代码
public String longestPalindrome(String s) {
    if (s == null || s.length() < 1) return "";
    
    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 left, int right) {
    while (left >= 0 && right < s.length() && 
           s.charAt(left) == s.charAt(right)) {
        left--;
        right++;
    }
    return right - left - 1;
}
编辑距离
java 复制代码
public int minDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[][] dp = new int[m + 1][n + 1];
    
    for (int i = 0; i <= m; i++) dp[i][0] = i;
    for (int j = 0; j <= n; j++) dp[0][j] = j;
    
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + Math.min(
                    dp[i - 1][j],      // 删除
                    Math.min(
                        dp[i][j - 1],  // 插入
                        dp[i - 1][j - 1]  // 替换
                    )
                );
            }
        }
    }
    
    return dp[m][n];
}

4. 动态规划经典题

打家劫舍
java 复制代码
public int rob(int[] nums) {
    if (nums.length == 0) return 0;
    if (nums.length == 1) return nums[0];
    
    int[] dp = new int[nums.length];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    
    for (int i = 2; i < nums.length; i++) {
        dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
    }
    
    return dp[nums.length - 1];
}

// 空间优化版本
public int robOptimized(int[] nums) {
    int prev2 = 0, prev1 = 0;
    for (int num : nums) {
        int temp = prev1;
        prev1 = Math.max(prev1, prev2 + num);
        prev2 = temp;
    }
    return prev1;
}
零钱兑换
java 复制代码
public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    Arrays.fill(dp, amount + 1);
    dp[0] = 0;
    
    for (int i = 1; i <= amount; i++) {
        for (int coin : coins) {
            if (coin <= i) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    
    return dp[amount] > amount ? -1 : dp[amount];
}

6. 图算法相关

岛屿数量
java 复制代码
public int numIslands(char[][] grid) {
    if (grid == null || grid.length == 0) return 0;
    
    int m = grid.length, n = grid[0].length;
    int count = 0;
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (grid[i][j] == '1') {
                dfs(grid, i, j);
                count++;
            }
        }
    }
    
    return count;
}

private void dfs(char[][] grid, int i, int j) {
    if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length ||
        grid[i][j] == '0') {
        return;
    }
    
    grid[i][j] = '0';  // 标记为已访问
    
    dfs(grid, i + 1, j);
    dfs(grid, i - 1, j);
    dfs(grid, i, j + 1);
    dfs(grid, i, j - 1);
}
合并区间
java 复制代码
public int[][] merge(int[][] intervals) {
    if (intervals.length <= 1) return intervals;
    
    // 按起始位置排序
    Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
    
    List<int[]> result = new ArrayList<>();
    int[] current = intervals[0];
    result.add(current);
    
    for (int[] interval : intervals) {
        if (interval[0] <= current[1]) {
            // 重叠,合并
            current[1] = Math.max(current[1], interval[1]);
        } else {
            // 不重叠,添加新区间
            current = interval;
            result.add(current);
        }
    }
    
    return result.toArray(new int[result.size()][]);
}
括号匹配
java 复制代码
public boolean isValid(String s) {
    Stack<Character> stack = new Stack<>();
    
    for (char c : s.toCharArray()) {
        if (c == '(' || c == '[' || c == '{') {
            stack.push(c);
        } else {
            if (stack.isEmpty()) return false;
            char top = stack.pop();
            if ((c == ')' && top != '(') ||
                (c == ']' && top != '[') ||
                (c == '}' && top != '{')) {
                return false;
            }
        }
    }
    
    return stack.isEmpty();
}

7. 字符串处理

字符串转整数(atoi)
java 复制代码
public int myAtoi(String s) {
    if (s == null || s.length() == 0) return 0;
    
    int i = 0;
    // 跳过空格
    while (i < s.length() && s.charAt(i) == ' ') i++;
    
    if (i >= s.length()) return 0;
    
    // 判断符号
    int sign = 1;
    if (s.charAt(i) == '+' || s.charAt(i) == '-') {
        sign = s.charAt(i) == '-' ? -1 : 1;
        i++;
    }
    
    long result = 0;
    while (i < s.length() && Character.isDigit(s.charAt(i))) {
        result = result * 10 + (s.charAt(i) - '0');
        
        if (sign * result > Integer.MAX_VALUE) {
            return Integer.MAX_VALUE;
        }
        if (sign * result < Integer.MIN_VALUE) {
            return Integer.MIN_VALUE;
        }
        
        i++;
    }
    
    return (int) (sign * result);
}
最长公共前缀
java 复制代码
public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) return "";
    
    String prefix = strs[0];
    for (int i = 1; i < strs.length; i++) {
        while (!strs[i].startsWith(prefix)) {
            prefix = prefix.substring(0, prefix.length() - 1);
            if (prefix.isEmpty()) return "";
        }
    }
    
    return prefix;
}

8. 数学算法

快速幂
java 复制代码
public long fastPower(long base, long exponent, long mod) {
    long result = 1;
    base = base % mod;
    
    while (exponent > 0) {
        if (exponent % 2 == 1) {
            result = (result * base) % mod;
        }
        exponent = exponent >> 1;
        base = (base * base) % mod;
    }
    
    return result;
}
最大公约数(欧几里得算法)
java 复制代码
// 递归版本
public int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

// 迭代版本
public int gcdIterative(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
判断质数
java 复制代码
public boolean isPrime(int n) {
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) return false;
    }
    return true;
}

5. 位运算技巧

java 复制代码
// 判断是否为2的幂
public boolean isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0;
}

// 计算1的个数
public int countBits(int n) {
    int count = 0;
    while (n != 0) {
        n &= (n - 1);  // 清除最低位的1
        count++;
    }
    return count;
}

// 只出现一次的数字(其他都出现两次)
public int singleNumber(int[] nums) {
    int result = 0;
    for (int num : nums) {
        result ^= num;  // 异或运算
    }
    return result;
}

高频面试问答(深度解析)

1. 时间复杂度和空间复杂度?

标准答案

  • 时间复杂度:算法执行时间随输入规模增长的趋势
  • 空间复杂度:算法所需内存空间随输入规模增长的趋势
  • 常见复杂度:O(1)、O(log n)、O(n)、O(n log n)、O(n²)、O(2ⁿ)

深入追问与回答思路

Q: 如何分析复杂度?

java 复制代码
// O(1):常数时间
int get(int[] arr, int index) {
    return arr[index];
}

// O(n):线性时间
int sum(int[] arr) {
    int sum = 0;
    for (int num : arr) {
        sum += num;
    }
    return sum;
}

// O(n²):平方时间
void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
            }
        }
    }
}

2. 如何选择合适的数据结构?

标准答案

  • 查找频繁:HashMap、TreeMap
  • 插入删除频繁:LinkedList
  • 需要排序:TreeSet、PriorityQueue
  • 需要去重:HashSet

深入追问与回答思路

Q: 实际应用场景?

java 复制代码
// 场景1:用户ID查找用户信息
// 选择:HashMap(O(1)查找)
Map<Long, User> userMap = new HashMap<>();

// 场景2:维护有序列表
// 选择:TreeSet(自动排序)
TreeSet<Integer> sortedSet = new TreeSet<>();

// 场景3:任务优先级队列
// 选择:PriorityQueue(堆实现)
PriorityQueue<Task> queue = new PriorityQueue<>(
    Comparator.comparing(Task::getPriority)
);

3. 如何优化算法性能?

标准答案

  • 减少时间复杂度:选择更优算法
  • 减少空间复杂度:原地算法、空间复用
  • 缓存结果:记忆化搜索
  • 并行处理:多线程、分治

深入追问与回答思路

Q: 优化示例?

java 复制代码
// 优化前:O(n²)
public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] == target) {
                return new int[]{i, j};
            }
        }
    }
    return null;
}

// 优化后:O(n)
public int[] twoSumOptimized(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 null;
}

延伸阅读

  • 《算法导论》《数据结构与算法分析》
  • LeetCode、牛客网算法练习
  • 《编程珠玑》《算法竞赛进阶指南》

发布建议:拆分为"数据结构基础""排序与查找""动态规划与贪心""面试算法题"四篇文章,每篇附代码示例和复杂度分析。

相关推荐
leaves falling2 小时前
数据结构-递归算法
数据结构
WizLC2 小时前
【后端】面向对象编程是什么(附加几个通用小实例项目)
java·服务器·后端·python·设计语言
刘个Java2 小时前
手搓遥控器通过上云api执行航线
java·redis·spring cloud·docker
Doro再努力2 小时前
【数据结构07】双向链表完结+栈
数据结构·链表
回吐泡泡oO2 小时前
ElasticSearch添加登录校验(仅供参考)
java·elasticsearch·jenkins
苏宸啊2 小时前
二叉树与堆:高效数据结构解析
数据结构
步步为营DotNet2 小时前
深度剖析.NET中WeakReference的内存管理机制:优化资源使用与避免内存泄漏
java·jvm·.net
郝学胜-神的一滴2 小时前
GLSL语法详解:从入门到实战
c++·算法·图形渲染
武子康2 小时前
Java-211 Spring Boot 2.4.1 整合 RabbitMQ 实战:DirectExchange + @RabbitListener 全流程
java·spring boot·分布式·消息队列·rabbitmq·rocketmq·java-rabbitmq