跳到正文
Joeplover
学习笔记·2026-06-20·约 4 分钟阅读

100 题训练启动 — Java 前 12 题

从 FizzBuzz 到水仙花数:前 12 道基础题的完成情况与暴露的编码问题。

Java 编程题

100 题训练启动 — Java 前 12 题

背景

这是"100 道基础题"训练计划的第一批题目。目标是通过编程题巩固 Java 基础语法和常用 API,同时练习防御式编程思维。

训练计划文件:F:/学习记录/100道基础题.md

题目概览(前 12 题)

题号题目考察点
1两数之和数组遍历、HashMap
2斐波那契数列递归/迭代、边界条件
3判断回文数字符串反转/数学方法
4最大公约数辗转相除法
5数组去重Set/双指针
6字符串压缩字符串遍历、StringBuilder
7括号匹配栈的应用
8二分查找有序数组、边界处理
9快速排序递归、分治
10合并两个有序数组双指针
11找出出现次数最多的元素HashMap 计数
12反转链表指针操作

两数之和(题 1)

题目:给定一个整数数组和目标值,返回两个数(索引)使它们之和等于目标值。

// 暴力法 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 new int[]{};
}

优化:用 HashMap 一次遍历,O(n):

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

防御式编程要点:

  • 数组为空或长度 < 2 时直接返回
  • 找不到结果也要返回(不要抛异常)
  • HashMap 的 put 语义:新值覆盖旧值,返回旧值

括号匹配(题 7)

题目:判断字符串中的括号是否成对匹配,如 (){}[] 合法,({)} 不合法。

public boolean isValid(String s) {
    if (s == null || s.length() % 2 != 0) return false;

    Map<Character, Character> pairs = new HashMap<>();
    pairs.put(')', '(');
    pairs.put('}', '{');
    pairs.put(']', '[');

    Deque<Character> stack = new ArrayDeque<>();

    for (char c : s.toCharArray()) {
        if (pairs.containsKey(c)) {
            // 如果是右括号,检查栈顶是否匹配
            if (stack.isEmpty() || stack.pop() != pairs.get(c)) {
                return false;
            }
        } else {
            // 如果是左括号,入栈
            stack.push(c);
        }
    }
    return stack.isEmpty();
}

反思: 这道题是栈的经典应用。之前学数据结构时知道 Stack 是什么,但遇到实际问题不会用。当前这题之后,再看 JSON 校验、HTML 标签闭合、表达式求值都是类似思路。

反转链表(题 12)

public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;

    while (curr != null) {
        ListNode next = curr.next;  // 保存下一个节点
        curr.next = prev;           // 反转指针
        prev = curr;                // 前移 prev
        curr = next;                // 前移 curr
    }
    return prev;  // 新的头节点
}

指针操作易错点:

  • 要在修改 curr.next 之前保存 next 节点
  • 循环结束后 prev 指向原链表的尾节点(即新链表的头)
  • 递归解法也可以,但递归深度受链表长度限制

训练心得

12 道题做下来,暴露了以下问题:

  1. HashMap 语义不清: put 返回旧值 vs 新值的混淆
  2. 边界条件遗漏: 数组空、长度不足、null 指针的检查缺失
  3. 栈的应用场景不熟悉: 知道 Stack 有什么方法,但不知道括号匹配该用栈
  4. 防御式编程不足: 写完代码只考虑正常情况,不考虑异常输入

这些都是写好生产代码的基本功——生产环境的数据永远不会像 LeetCode 输入那样规整。