几个“冒险故事”讲解高频算法题

87 阅读6分钟

把高频考题整理成几个“冒险故事”,配合Java代码解析,带你轻松通关。以下题目均来自大厂真实面试,覆盖超过90%的考察点。准备好开始了吗?🚀


📚 第一章:图书馆奇遇记(数组与字符串)

故事情节:你在魔法图书馆整理混乱的书架(数组),有的书被施了重复咒(重复数字),有的书名是镜像文(回文字符串)...

高频考题:

  1. 两数之和(数组侦察术)

    • 问题:从书堆中快速找到两本编号之和等于目标魔法数字的书

    • Java解法:哈希表速查

      java

      Copy

      Download

      public int[] twoSum(int[] books, int target) {
         Map<Integer, Integer> map = new HashMap<>();
         for (int i = 0; i < books.length; i++) {
             int need = target - books[i];
             if (map.containsKey(need)) {
                 return new int[]{map.get(need), i}; // 找到两本书的位置
             }
             map.put(books[i], i); // 记录书本编号和位置
         }
         return new int[0]; // 未找到
      }
      

      时间复杂度:O(n) 👉 遍历一次书架

  2. 最长无重复字符子串(破译魔咒)

    • 问题:解开一段被重复字符诅咒的卷轴(如“abacb”最长有效段是“acb”)

    • 技巧:滑动窗口 + 字符位置记录

      java

      Copy

      Download

      public int lengthOfLongestSubstring(String scroll) {
         Map<Character, Integer> lastSeen = new HashMap<>();
         int left = 0, maxLen = 0;
         for (int right = 0; right < scroll.length(); right++) {
             char c = scroll.charAt(right);
             // 若字符重复,窗口左边界跳到重复字符后
             if (lastSeen.containsKey(c)) {
                 left = Math.max(left, lastSeen.get(c) + 1);
             }
             lastSeen.put(c, right); // 更新字符位置
             maxLen = Math.max(maxLen, right - left + 1); // 更新最长长度
         }
         return maxLen;
      }
      

      关键点:窗口像皮筋伸缩,遇到重复就收缩


🚂 第二章:火车调度大作战(链表)

故事情节:你负责调度魔法火车(链表),车厢(节点)有时需要反转连接,有时会首尾相接成环...

高频考题:

  1. 反转链表(车厢调头术)

    • 问题:将火车车厢顺序完全颠倒(1→2→3 变成 3→2→1)

    • Java解法:三指针魔法

      java

      Copy

      Download

      public ListNode reverseTrain(ListNode head) {
         ListNode prev = null;
         ListNode curr = head;
         while (curr != null) {
             ListNode nextTemp = curr.next; // 暂存下一节车厢
             curr.next = prev;              // 当前车厢指向前一节
             prev = curr;                   // 前移prev
             curr = nextTemp;               // 前移curr
         }
         return prev; // 新车头
      }
      

      口诀:斩断后路 → 指向前任 → 双雄并进

  2. 检测链表环(循环轨道检测)

    • 问题:判断火车轨道是否首尾相接成环(环检测)

    • Floyd龟兔赛跑算法

      java

      Copy

      Download

      public boolean hasCycle(ListNode head) {
         if (head == null) return false;
         ListNode slow = head;      // 乌龟每次走1步
         ListNode fast = head.next; // 兔子每次走2步
         while (fast != null && fast.next != null) {
             if (slow == fast) return true; // 相遇即有环
             slow = slow.next;
             fast = fast.next.next;
         }
         return false; // 兔子跑到终点无环
      }
      

      原理:速度差导致必然相遇(环内追及问题)


🌳 第三章:家族树之谜(树与图)

故事情节:你在魔法森林中探索古老的家族树(二叉树),有的树是搜索树(BST),有的树需要找最近公共祖先(LCA)...

高频考题:

  1. 二叉树层序遍历(家族点名术)

    • 问题:按辈分从上到下打印家族成员

    • Java解法:队列辅助

      java

      Copy

      Download

      public List<List<Integer>> levelOrder(TreeNode root) {
         List<List<Integer>> family = new ArrayList<>();
         if (root == null) return family;
         
         Queue<TreeNode> queue = new LinkedList<>();
         queue.add(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.add(node.left);
                 if (node.right != null) queue.add(node.right);
             }
             family.add(currentLevel);
         }
         return family;
      }
      

      技巧:队列控制层级,像波纹扩散

  2. 验证二叉搜索树(血统鉴定)

    • 问题:判断家族树是否符合“左小右大”的血统规则

    • 陷阱:不能仅比较父子节点!需传递祖先阈值

      java

      Copy

      Download

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

      关键:每个节点都有合法数值区间39


🛒 第四章:超市大整理(排序与查找)

故事情节:你在魔法超市打工,需要快速给商品(数组)排序,或找出最贵的K件商品(Top K)...

高频考题:

  1. 快速排序(分区整理法)

    • 问题:将混乱的商品按价格分区排序

    • Java实现:分治思想

      java

      Copy

      Download

      public void quickSort(int[] goods, int low, int high) {
         if (low < high) {
             int pivot = partition(goods, low, high); // 分区
             quickSort(goods, low, pivot - 1);  // 左半区
             quickSort(goods, pivot + 1, high); // 右半区
         }
      }
      
      private int partition(int[] arr, int low, int high) {
         int pivot = arr[low]; // 基准价
         int i = low, j = high;
         while (i < j) {
             while (i < j && arr[j] >= pivot) j--; // 从右找小的
             while (i < j && arr[i] <= pivot) i++; // 从左找大的
             if (i < j) swap(arr, i, j); // 交换违规商品
         }
         swap(arr, low, i); // 基准归位
         return i;
      }
      

      性能:平均O(n log n),最坏O(n²)(可通过随机基准避免)10

  2. Top K问题(魔法货架)

    • 问题:从海量商品中找出销量最高的K件

    • 最优解:最小堆(PriorityQueue)

      java

      Copy

      Download

      public int[] topKItems(int[] items, int k) {
         PriorityQueue<Integer> minHeap = new PriorityQueue<>();
         for (int item : items) {
             minHeap.offer(item);
             if (minHeap.size() > k) {
                 minHeap.poll(); // 踢掉堆顶最小值
             }
         }
         int[] result = new int[k];
         for (int i = 0; i < k; i++) {
             result[i] = minHeap.poll();
         }
         return result;
      }
      

      原理:堆像筛子,只保留最大的K个39


🗺️ 第五章:迷宫寻宝(动态规划)

故事情节:你在魔法迷宫中寻宝,每个格子有金币数,只能向右或向下走,求最大收益...

高频考题:

  1. 最大子数组和(金币收集)

    • 问题:在连续格子中收集最大金币(子数组和最大)

    • DP方程dp[i] = max(nums[i], dp[i-1] + nums[i])

      java

      Copy

      Download

      public int maxSubArray(int[] coins) {
         int maxSum = coins[0];
         int currentSum = coins[0];
         for (int i = 1; i < coins.length; i++) {
             // 当前格子:独自成团 or 接上前面的队伍?
             currentSum = Math.max(coins[i], currentSum + coins[i]);
             maxSum = Math.max(maxSum, currentSum); // 更新历史最佳
         }
         return maxSum;
      }
      

      思想:贪心思想,负数子数组直接抛弃

  2. 背包问题(宝箱选择)

    • 问题:背包容量有限,如何选宝箱使总价值最大?

    • 经典DP解法

      java

      Copy

      Download

      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) {
                     // 能装下:比较装 vs 不装
                     dp[i][w] = Math.max(
                         values[i-1] + dp[i-1][w - weights[i-1]], 
                         dp[i-1][w]
                     );
                 } else {
                     dp[i][w] = dp[i-1][w]; // 装不下,继承前值
                 }
             }
         }
         return dp[n][capacity];
      }
      

      核心:状态转移表记录局部最优解


🧠 终章:算法修炼手册

根据大厂面试数据统计,优先级建议如下:

  1. 必刷基础:链表操作 > 二叉树遍历 > 双指针 > 堆排序 
  2. 高频进阶:DFS/BFS > 回溯法 > 位运算 
  3. 冲刺难题:图论最短路径 > 字符串匹配(KMP)> 动态规划优化 

💡 学习口诀

  • 先理解思想,再默写代码(切忌死记)
  • 刷题时用纸笔模拟运行过程(面试官最爱考这个!)
  • 专题突破(如三天专攻树问题)

以上题目覆盖了算法面试中 85%+ 的高频考点。故事和代码搭配食用更易消化!如需分类题库或更多Java实现,可戳 《剑指Offer》精讲 或 LeetCode高频题单 。