Skip to content

LeetCode Hot 100 (51-80)

title51 - 岛屿数量

NumberOfIslands.java

java
package leecode100.title51;

/**
 * 题目:岛屿数量
 * 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
 * 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
 * 此外,你可以假设该网格的四条边均被水包围。
 *
 * 示例 1:
 * 输入:grid = [
 *   ["1","1","1","1","0"],
 *   ["1","1","0","1","0"],
 *   ["1","1","0","0","0"],
 *   ["0","0","0","0","0"]
 * ]
 * 输出:1
 *
 * 示例 2:
 * 输入:grid = [
 *   ["1","1","0","0","0"],
 *   ["1","1","0","0","0"],
 *   ["0","0","1","0","0"],
 *   ["0","0","0","1","1"]
 * ]
 * 输出:3
 */
public class NumberOfIslands {
    /**
     * 解题思路:
     * 1. 遍历整个网格。
     * 2. 当遇到 '1' 时,岛屿数量加 1,并以此为起点进行深度优先搜索 (DFS)。
     * 3. 在 DFS 过程中,将所有相连的 '1' 标记为 '0'(或其它标记),以避免重复计数。
     * 4. 最终返回岛屿的总数。
     */
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int numIslands = 0;
        int rows = grid.length;
        int cols = grid[0].length;

        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == '1') {
                    numIslands++;
                    dfs(grid, r, c);
                }
            }
        }

        return numIslands;
    }

    private void dfs(char[][] grid, int r, int c) {
        int rows = grid.length;
        int cols = grid[0].length;

        if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] == '0') {
            return;
        }

        grid[r][c] = '0'; // 标记为已访问
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    public static void main(String[] args) {
        NumberOfIslands solution = new NumberOfIslands();
        char[][] grid1 = {
            {'1', '1', '1', '1', '0'},
            {'1', '1', '0', '1', '0'},
            {'1', '1', '0', '0', '0'},
            {'0', '0', '0', '0', '0'}
        };
        System.out.println("Example 1: " + solution.numIslands(grid1)); // Output: 1

        char[][] grid2 = {
            {'1', '1', '0', '0', '0'},
            {'1', '1', '0', '0', '0'},
            {'0', '0', '1', '0', '0'},
            {'0', '0', '0', '1', '1'}
        };
        System.out.println("Example 2: " + solution.numIslands(grid2)); // Output: 3
    }
}

title52 - 腐烂的橘子

RottingOranges.java

java
package leecode100.title52;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 题目:腐烂的橘子
 * 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
 * 值 0 代表空单元格;
 * 值 1 代表新鲜橘子;
 * 值 2 代表腐烂的橘子。
 * 每分钟,腐烂的橘子 周围 4 个方向上相邻的新鲜橘子都会腐烂。
 * 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
 *
 * 示例 1:
 * 输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
 * 输出:4
 */
public class RottingOranges {
    /**
     * 解题思路:
     * 1. 使用广度优先搜索 (BFS)。
     * 2. 首先将所有腐烂的橘子 (2) 的坐标加入队列,并统计新鲜橘子 (1) 的总数。
     * 3. 每一层 BFS 代表一分钟,将队列中腐烂橘子四周的新鲜橘子变腐烂,并加入队列。
     * 4. 记录经过的分钟数。
     * 5. 最后检查是否还有新鲜橘子。
     */
    public int orangesRotting(int[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        int freshCount = 0;

        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == 2) {
                    queue.offer(new int[]{r, c});
                } else if (grid[r][c] == 1) {
                    freshCount++;
                }
            }
        }

        if (freshCount == 0) return 0;

        int minutes = 0;
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        while (!queue.isEmpty()) {
            int size = queue.size();
            boolean rottedAny = false;
            for (int i = 0; i < size; i++) {
                int[] curr = queue.poll();
                for (int[] dir : directions) {
                    int nr = curr[0] + dir[0];
                    int nc = curr[1] + dir[1];
                    if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc] == 1) {
                        grid[nr][nc] = 2;
                        queue.offer(new int[]{nr, nc});
                        freshCount--;
                        rottedAny = true;
                    }
                }
            }
            if (rottedAny) minutes++;
        }

        return freshCount == 0 ? minutes : -1;
    }

    public static void main(String[] args) {
        RottingOranges solution = new RottingOranges();
        int[][] grid = {{2, 1, 1}, {1, 1, 0}, {0, 1, 1}};
        System.out.println("Minutes: " + solution.orangesRotting(grid)); // Output: 4
    }
}

title53 - 课程表

CourseSchedule.java

java
package leecode100.title53;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 题目:课程表
 * 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
 * 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
 * 给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。
 * 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
 *
 * 示例 1:
 * 输入:numCourses = 2, prerequisites = [[1,0]]
 * 输出:true
 */
public class CourseSchedule {
    /**
     * 解题思路:
     * 1. 这是一个拓扑排序问题,判断有向图中是否存在环。
     * 2. 使用入度数组记录每门课的先修课程数量。
     * 3. 使用邻接表存储图。
     * 4. 将所有入度为 0 的课程加入队列。
     * 5. 依次从队列中取出课程,将其指向的后续课程入度减 1。如果后续课程入度变为 0,则加入队列。
     * 6. 最后检查学习过的课程数量是否等于总课程数。
     */
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] inDegree = new int[numCourses];
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            adj.add(new ArrayList<>());
        }

        for (int[] pre : prerequisites) {
            inDegree[pre[0]]++;
            adj.get(pre[1]).add(pre[0]);
        }

        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }

        int count = 0;
        while (!queue.isEmpty()) {
            int curr = queue.poll();
            count++;
            for (int next : adj.get(curr)) {
                inDegree[next]--;
                if (inDegree[next] == 0) {
                    queue.offer(next);
                }
            }
        }

        return count == numCourses;
    }

    public static void main(String[] args) {
        CourseSchedule solution = new CourseSchedule();
        int[][] pre = {{1, 0}};
        System.out.println("Can finish: " + solution.canFinish(2, pre)); // Output: true
    }
}

title54 - 实现 Trie (前缀树)

ImplementTriePrefixTree.java

java
package leecode100.title54;

/**
 * 题目:实现 Trie (前缀树)
 * Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。
 * 这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
 * 请你实现 Trie 类:
 * Trie() 初始化前缀树对象。
 * void insert(String word) 向前缀树中插入字符串 word 。
 * boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
 * boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
 * 
 * 示例 1:
 * 输入
 * ["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
 * [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
 * 输出
 * [null, null, true, false, true, null, true]
 * 
 * 解释
 * Trie trie = new Trie();
 * trie.insert("apple");
 * trie.search("apple");   // 返回 True
 * trie.search("app");     // 返回 False
 * trie.startsWith("app"); // 返回 True
 * trie.insert("app");
 * trie.search("app");     // 返回 True
 * 
 * 提示:
 * 1 <= word.length, prefix.length <= 2000
 * word 和 prefix 仅由小写英文字母组成
 * insert、search 和 startsWith 调用次数 总计 不超过 3 * 10^4 次
 */
public class ImplementTriePrefixTree {
    /**
     * 解题思路:
     * 三个核心操作
     *
     * 插入单词 (insert)
     * 从根节点开始,逐个字符遍历
     * 字符不存在就新建节点,存在就继续往下走
     * 单词结尾节点标记 isEnd = true
     *
     * 查找单词 (search)
     * 先找到单词前缀对应的节点
     * 检查该节点是否存在且是单词结尾(isEnd为true)
     *
     * 查找前缀 (startsWith)
     * 只需要找到前缀对应的节点即可
     * 只要节点存在就说明前缀存在
     */

    static class Trie {
        Trie[] children;
        boolean isEnd;
        
        public Trie() {
            children = new Trie[26];
            isEnd = false;
        }

        public void insert(String word) {
            Trie node = this;
            for (char c : word.toCharArray()) {
                int index = c - 'a';
                if (node.children[index] == null) {
                    node.children[index] = new Trie();
                }
                node = node.children[index];
                /**
                 * 代码解析
                 * node: 当前遍历到的树节点
                 * node.children: 当前节点的子节点数组或列表
                 * index: 用于定位特定子节点的索引
                 * node.children[index]: 获取当前节点的第 index 个子节点
                 * 整体含义: 将当前节点移动到指定的子节点
                 */
            }
            node.isEnd = true;
        }

        public boolean search(String word) {
            Trie node = searchPrefix(word);
            return node != null && node.isEnd;
        }

        public boolean startsWith(String prefix) {
            return searchPrefix(prefix) != null;
        }

        private Trie searchPrefix(String prefix) {
            Trie node = this;
            for (char c : prefix.toCharArray()) {
                int index = c - 'a';
                if (node.children[index] == null) {
                    return null;
                }
                node = node.children[index];
            }
            return node;
        }
    }

    public static void main(String[] args) {
        // Test case from example
        ImplementTriePrefixTree implementTrie = new ImplementTriePrefixTree();
        Trie trie = new Trie();
        
        trie.insert("apple");
        System.out.println(trie.search("apple"));   // return True
        System.out.println(trie.search("app"));     // return False
        System.out.println(trie.startsWith("app")); // return True
        
        trie.insert("app");
        System.out.println(trie.search("app"));     // return True
    }
}

title55 - 全排列

Permutations.java

java
package leecode100.title55;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:全排列
 * 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
 *
 * 示例 1:
 * 输入:nums = [1,2,3]
 * 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
 */
public class Permutations {
    /**
     * 解题思路:
     * 1. 使用回溯算法。
     * 2. 维护一个 used 数组记录哪些数字已经被使用。
     * 3. 递归地选择未使用的数字加入当前路径。
     * 4. 当路径长度等于数组长度时,将路径加入结果集。
     */
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        boolean[] used = new boolean[nums.length];
        backtrack(nums, used, path, res);
        return res;
    }

    private void backtrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> res) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (!used[i]) {
                used[i] = true;
                path.add(nums[i]);
                backtrack(nums, used, path, res);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
    }

    public static void main(String[] args) {
        Permutations solution = new Permutations();
        int[] nums = {1, 2, 3};
        System.out.println(solution.permute(nums));
    }
}

title56 - 子集

Subsets.java

java
package leecode100.title56;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:子集
 * 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
 * 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
 *
 * 示例 1:
 * 输入:nums = [1,2,3]
 * 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
 */
public class Subsets {
    /**
     * 解题思路:
     * 1. 使用回溯算法。
     * 2. 每次递归都将当前路径加入结果集(因为每个节点都是一个子集)。
     * 3. 从当前索引开始向后遍历,选择元素加入路径并递归。
     */
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(0, nums, new ArrayList<>(), res);
        return res;
    }

    private void backtrack(int start, int[] nums, List<Integer> path, List<List<Integer>> res) {
        res.add(new ArrayList<>(path));

        for (int i = start; i < nums.length; i++) {
            path.add(nums[i]);
            backtrack(i + 1, nums, path, res);
            path.remove(path.size() - 1);
        }
    }

    public static void main(String[] args) {
        Subsets solution = new Subsets();
        int[] nums = {1, 2, 3};
        System.out.println(solution.subsets(nums));
    }
}

title57 - 电话号码的字母组合

LetterCombinationsOfAPhoneNumber.java

java
package leecode100.title57;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:电话号码的字母组合
 * 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
 * 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
 *
 * 示例 1:
 * 输入:digits = "23"
 * 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
 */
public class LetterCombinationsOfAPhoneNumber {
    private String[] map = {
        "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
    };

    public List<String> letterCombinations(String digits) {
        List<String> res = new ArrayList<>();
        if (digits == null || digits.length() == 0) return res;
        backtrack(0, digits, new StringBuilder(), res);
        return res;
    }

    private void backtrack(int index, String digits, StringBuilder sb, List<String> res) {
        if (index == digits.length()) {
            res.add(sb.toString());
            return;
        }

        String letters = map[digits.charAt(index) - '0'];
        for (char c : letters.toCharArray()) {
            sb.append(c);
            backtrack(index + 1, digits, sb, res);
            sb.deleteCharAt(sb.length() - 1);
        }
    }

    public static void main(String[] args) {
        LetterCombinationsOfAPhoneNumber solution = new LetterCombinationsOfAPhoneNumber();
        System.out.println(solution.letterCombinations("23"));
    }
}

title58 - 组合总和

CombinationSum.java

java
package leecode100.title58;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:组合总和
 * 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,
 * 找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。
 * candidates 中的 同一个 数字可以 无限制重复被选取 。
 *
 * 示例 1:
 * 输入:candidates = [2,3,6,7], target = 7
 * 输出:[[2,2,3],[7]]
 */
public class CombinationSum {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(0, candidates, target, new ArrayList<>(), res);
        return res;
    }

    private void backtrack(int start, int[] candidates, int target, List<Integer> path, List<List<Integer>> res) {
        if (target < 0) return;
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            path.add(candidates[i]);
            // 因为可以重复使用,所以传 i 而不是 i + 1
            backtrack(i, candidates, target - candidates[i], path, res);
            path.remove(path.size() - 1);
        }
    }

    public static void main(String[] args) {
        CombinationSum solution = new CombinationSum();
        int[] candidates = {2, 3, 6, 7};
        System.out.println(solution.combinationSum(candidates, 7));
    }
}

title59 - 括号生成

GenerateParentheses.java

java
package leecode100.title59;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:括号生成
 * 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
 *
 * 示例 1:
 * 输入:n = 3
 * 输出:["((()))","(()())","(())()","()(())","()()()"]
 */
public class GenerateParentheses {
    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<>();
        backtrack(0, 0, n, new StringBuilder(), res);
        return res;
    }

    private void backtrack(int left, int right, int n, StringBuilder sb, List<String> res) {
        if (sb.length() == n * 2) {
            res.add(sb.toString());
            return;
        }

        if (left < n) {
            sb.append('(');
            backtrack(left + 1, right, n, sb, res);
            sb.deleteCharAt(sb.length() - 1);
        }
        if (right < left) {
            sb.append(')');
            backtrack(left, right + 1, n, sb, res);
            sb.deleteCharAt(sb.length() - 1);
        }
    }

    public static void main(String[] args) {
        GenerateParentheses solution = new GenerateParentheses();
        System.out.println(solution.generateParenthesis(3));
    }
}

title60 - 单词搜索

WordSearch.java

java
package leecode100.title60;

/**
 * 题目:单词搜索
 * 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。
 * 如果 word 存在于网格中,返回 true ;否则,返回 false 。
 * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。
 * 同一个单元格内的字母不允许被重复使用。
 *
 * 示例 1:
 * 输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
 * 输出:true
 */
public class WordSearch {
    public boolean exist(char[][] board, String word) {
        int rows = board.length;
        int cols = board[0].length;
        boolean[][] visited = new boolean[rows][cols];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (dfs(board, word, 0, i, j, visited)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(char[][] board, String word, int index, int r, int c, boolean[][] visited) {
        if (index == word.length()) return true;
        if (r < 0 || r >= board.length || c < 0 || c >= board[0].length 
            || visited[r][c] || board[r][c] != word.charAt(index)) {
            return false;
        }

        visited[r][c] = true;
        boolean res = dfs(board, word, index + 1, r - 1, c, visited)
                   || dfs(board, word, index + 1, r + 1, c, visited)
                   || dfs(board, word, index + 1, r, c - 1, visited)
                   || dfs(board, word, index + 1, r, c + 1, visited);
        visited[r][c] = false;
        return res;
    }

    public static void main(String[] args) {
        WordSearch solution = new WordSearch();
        char[][] board = {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}};
        System.out.println(solution.exist(board, "ABCCED")); // true
    }
}

title61 - 分割回文串

PalindromePartitioning.java

java
package leecode100.title61;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:分割回文串
 * 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
 * 回文串 是正着读和反着读都一样的字符串。
 * 
 * 示例 1:
 * 输入:s = "aab"
 * 输出:[["a","a","b"],["aa","b"]]
 * 
 * 示例 2:
 * 输入:s = "a"
 * 输出:[["a"]]
 * 
 * 提示:
 * 1 <= s.length <= 16
 * s 仅由小写英文字母组成
 */
public class PalindromePartitioning {
    /**
     * 解题思路:
     * 1. 使用回溯算法
     * 2. 从字符串的起始位置开始,尝试所有可能的分割点
     * 3. 如果当前分割出的子串是回文串,则递归处理剩余部分
     * 4. 当处理到字符串末尾时,将当前分割方案加入结果集
     */

    public static void main(String[] args) {
        PalindromePartitioning solution = new PalindromePartitioning();
        String s1 = "aab";
        List<List<String>> result1 = solution.partition(s1);
        System.out.println("Test case 1 result: " + result1);

        String s2 = "a";
        List<List<String>> result2 = solution.partition(s2);
        System.out.println("Test case 2 result: " + result2);
    }

    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        backtrack(s, 0, new ArrayList<>(), res);
        return res;
    }

    private void backtrack(String s, int start, List<String> path, List<List<String>> res) {
        if (start == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = start; i < s.length(); i++) {
            if (isPalindrome(s, start, i)) {
                path.add(s.substring(start, i + 1));
                backtrack(s, i + 1, path, res);
                path.remove(path.size() - 1);
            }
        }
    }

    private boolean isPalindrome(String s, int left, int right) {
        while (left < right) {
            if (s.charAt(left++) != s.charAt(right--)) {
                return false;
            }
        }
        return true;
    }
}

title62 - N 皇后

NQueens.java

java
package leecode100.title62;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 题目:N 皇后
 * 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
 * n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
 * 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
 * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 * 
 * 示例 1:
 * 输入:n = 4
 * 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 * 
 * 示例 2:
 * 输入:n = 1
 * 输出:[["Q"]]
 * 
 * 提示:
 * 1 <= n <= 9
 */
public class NQueens {
    /**
     * 解题思路:
     * 1. 使用回溯算法,逐行放置皇后
     * 2. 对于每一行,尝试在每一列放置皇后
     * 3. 放置前检查当前位置是否合法(同列、左上方、右上方是否有皇后)
     * 4. 如果合法,则放置皇后并递归处理下一行
     * 5. 找到一个解后,将棋盘状态转换为要求的格式并存入结果集
     */

    public static void main(String[] args) {
        NQueens solution = new NQueens();
        int n1 = 4;
        List<List<String>> result1 = solution.solveNQueens(n1);
        System.out.println("Test case 1 (n=4) solutions count: " + result1.size());
        for (List<String> sol : result1) {
            System.out.println(sol);
        }

        int n2 = 1;
        List<List<String>> result2 = solution.solveNQueens(n2);
        System.out.println("Test case 2 (n=1) result: " + result2);
    }

    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        char[][] board = new char[n][n];
        for (char[] row : board) {
            Arrays.fill(row, '.');
        }
        backtrack(board, 0, res);
        return res;
    }

    private void backtrack(char[][] board, int row, List<List<String>> res) {
        if (row == board.length) {
            res.add(construct(board));
            return;
        }

        for (int col = 0; col < board.length; col++) {
            if (isValid(board, row, col)) {
                board[row][col] = 'Q';
                backtrack(board, row + 1, res);
                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;
    }

    private List<String> construct(char[][] board) {
        List<String> path = new ArrayList<>();
        for (char[] row : board) {
            path.add(new String(row));
        }
        return path;
    }
}

title63 - 搜索插入位置

SearchInsertPosition.java

java
package leecode100.title63;

/**
 * 题目:搜索插入位置
 * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
 * 如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
 * 请必须使用时间复杂度为 O(log n) 的算法。
 * 
 * 示例 1:
 * 输入: nums = [1,3,5,6], target = 5
 * 输出: 2
 * 
 * 示例 2:
 * 输入: nums = [1,3,5,6], target = 2
 * 输出: 1
 * 
 * 示例 3:
 * 输入: nums = [1,3,5,6], target = 7
 * 输出: 4
 * 
 * 提示:
 * 1 <= nums.length <= 10^4
 * -10^4 <= nums[i] <= 10^4
 * nums 为 无重复元素 的 升序 排列数组
 * -10^4 <= target <= 10^4
 */
public class SearchInsertPosition {
    /**
     * 解题思路:
     * 1. 标准的二分查找
     * 2. 如果找到目标值,直接返回索引
     * 3. 如果没找到,最后 left 指针所在的位置就是插入位置
     */

    public static void main(String[] args) {
        SearchInsertPosition solution = new SearchInsertPosition();
        int[] nums = {1,3,5,6};
        System.out.println("Test case 1 (target=5): " + solution.searchInsert(nums, 5)); // 2
        System.out.println("Test case 2 (target=2): " + solution.searchInsert(nums, 2)); // 1
        System.out.println("Test case 3 (target=7): " + solution.searchInsert(nums, 7)); // 4
    }

    public int searchInsert(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;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}

title64 - 搜索二维矩阵

SearchA2DMatrix.java

java
package leecode100.title64;

/**
 * 题目:搜索二维矩阵
 * 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
 * 每行中的整数从左到右按升序排列。
 * 每行的第一个整数大于前一行的最后一个整数。
 * 
 * 示例 1:
 * 输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
 * 输出:true
 * 
 * 示例 2:
 * 输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
 * 输出:false
 * 
 * 提示:
 * m == matrix.length
 * n == matrix[i].length
 * 1 <= m, n <= 100
 * -10^4 <= matrix[i][j], target <= 10^4
 */
public class SearchA2DMatrix {
    /**
     * 解题思路:
     * 1. 由于矩阵的特性,可以将二维矩阵看作一个一维有序数组
     * 2. 使用二分查找
     * 3. 关键在于一维索引与二维坐标的转换:
     *    row = index / n
     *    col = index % n
     */

    public static void main(String[] args) {
        SearchA2DMatrix solution = new SearchA2DMatrix();
        int[][] matrix = {{1,3,5,7},{10,11,16,20},{23,30,34,60}};
        System.out.println("Test case 1 (target=3): " + solution.searchMatrix(matrix, 3)); // true
        System.out.println("Test case 2 (target=13): " + solution.searchMatrix(matrix, 13)); // false
    }

    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int left = 0, right = m * n - 1;

        while (left <= right) {
            int mid = left + (right - left) / 2;
            int val = matrix[mid / n][mid % n];
            if (val == target) {
                return true;
            } else if (val < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return false;
    }
}

title65 - 在排序数组中查找元素的第一个和最后一个位置

FindFirstAndLastPositionOfElementInSortedArray.java

java
package leecode100.title65;

import java.util.Arrays;

/**
 * 题目:在排序数组中查找元素的第一个和最后一个位置
 * 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。
 * 请你找出给定目标值在数组中的开始位置和结束位置。
 * 如果数组中不存在目标值 target,返回 [-1, -1]。
 * 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
 * 
 * 示例 1:
 * 输入:nums = [5,7,7,8,8,10], target = 8
 * 输出:[3,4]
 * 
 * 示例 2:
 * 输入:nums = [5,7,7,8,8,10], target = 6
 * 输出:[-1,-1]
 */
public class FindFirstAndLastPositionOfElementInSortedArray {
    /**
     * 解题思路:
     * 1. 使用两次二分查找
     * 2. 第一次查找第一个大于等于 target 的位置(左边界)
     * 3. 第二次查找第一个大于 target 的位置,减 1 即为右边界
     */

    public static void main(String[] args) {
        FindFirstAndLastPositionOfElementInSortedArray solution = new FindFirstAndLastPositionOfElementInSortedArray();
        int[] nums = {5,7,7,8,8,10};
        System.out.println("Test case 1 (target=8): " + Arrays.toString(solution.searchRange(nums, 8))); // [3, 4]
        System.out.println("Test case 2 (target=6): " + Arrays.toString(solution.searchRange(nums, 6))); // [-1, -1]
    }

    public int[] searchRange(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        }
        return new int[]{-1, -1};
    }

    private int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length - 1, ans = nums.length;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid - 1;
                ans = mid;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}

title66 - 搜索旋转排序数组

SearchInRotatedSortedArray.java

java
package leecode100.title66;

/**
 * 题目:搜索旋转排序数组
 * 整数数组 nums 按升序排列,数组中的值 互不相同 。
 * 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转。
 * 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
 * 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
 * 
 * 示例 1:
 * 输入:nums = [4,5,6,7,0,1,2], target = 0
 * 输出:4
 */
public class SearchInRotatedSortedArray {
    /**
     * 解题思路:
     * 1. 二分查找
     * 2. 关键在于:旋转后的数组,mid 将数组分成两部分,其中一部分必定是有序的
     * 3. 判断哪一部分有序,然后判断 target 是否在有序部分内
     */

    public static void main(String[] args) {
        SearchInRotatedSortedArray solution = new SearchInRotatedSortedArray();
        int[] nums = {4,5,6,7,0,1,2};
        System.out.println("Test case 1 (target=0): " + solution.search(nums, 0)); // 4
    }

    public int search(int[] nums, int target) {
        int n = nums.length;
        if (n == 0) return -1;
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            if (nums[0] <= nums[mid]) { // 左边有序
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else { // 右边有序
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
}

title67 - 寻找旋转排序数组中的最小值

FindMinimumInRotatedSortedArray.java

java
package leecode100.title67;

/**
 * 题目:寻找旋转排序数组中的最小值
 * 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。
 * 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。
 * 请你找出并返回数组中的 最小元素 。
 * 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
 * 
 * 示例 1:
 * 输入:nums = [3,4,5,1,2]
 * 输出:1
 */
public class FindMinimumInRotatedSortedArray {
    /**
     * 解题思路:
     * 1. 二分查找
     * 2. 比较 nums[mid] 和 nums[right]
     * 3. 如果 nums[mid] < nums[right],说明最小值在左侧(包含 mid)
     * 4. 如果 nums[mid] > nums[right],说明最小值在右侧(不包含 mid)
     */

    public static void main(String[] args) {
        FindMinimumInRotatedSortedArray solution = new FindMinimumInRotatedSortedArray();
        int[] nums = {3,4,5,1,2};
        System.out.println("Minimum: " + solution.findMin(nums)); // 1
    }

    public int findMin(int[] nums) {
        int low = 0, high = nums.length - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            } else {
                low = pivot + 1;
            }
        }
        return nums[low];
    }
}

title68 - 寻找两个正序数组的中位数

MedianOfTwoSortedArrays.java

java
package leecode100.title68;

/**
 * 题目:寻找两个正序数组的中位数
 * 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
 * 请你找出并返回这两个正序数组的 中位数 。
 * 算法的时间复杂度应该为 O(log (m+n)) 。
 * 
 * 示例 1:
 * 输入:nums1 = [1,3], nums2 = [2]
 * 输出:2.00000
 * 
 * 示例 2:
 * 输入:nums1 = [1,2], nums2 = [3,4]
 * 输出:2.50000
 */
public class MedianOfTwoSortedArrays {
    /**
     * 解题思路:
     * 1. 寻找第 k 小的数
     * 2. 每次比较两个数组中第 k/2 个元素,排除较小的那一部分
     * 3. 递归处理,直到 k=1 或其中一个数组为空
     */

    public static void main(String[] args) {
        MedianOfTwoSortedArrays solution = new MedianOfTwoSortedArrays();
        int[] nums1 = {1,3}, nums2 = {2};
        System.out.println("Median: " + solution.findMedianSortedArrays(nums1, nums2)); // 2.0
    }

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        int left = (n + m + 1) / 2;
        int right = (n + m + 2) / 2;
        return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + 
                getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;
    }

    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];
        if (k == 1) return Math.min(nums1[start1], nums2[start2]);

        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (nums1[i] > nums2[j]) {
            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        } else {
            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }
}

title69 - 有效的括号

ValidParentheses.java

java
package leecode100.title69;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * 题目:有效的括号
 * 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
 * 有效字符串需满足:
 * 左括号必须用相同类型的右括号闭合。
 * 左括号必须以正确的顺序闭合。
 * 每个右括号都有一个对应的相同类型的左括号。
 * 
 * 示例 1:
 * 输入:s = "()"
 * 输出:true
 */
public class ValidParentheses {
    /**
     * 解题思路:
     * 1. 使用栈
     * 2. 遇到左括号入栈
     * 3. 遇到右括号,检查栈顶是否匹配
     */

    public static void main(String[] args) {
        ValidParentheses solution = new ValidParentheses();
        System.out.println("Is valid: " + solution.isValid("()[]{}")); // true
    }

    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) return false;
        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque<Character> stack = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

title70 - 最小栈

MinStack.java

java
package leecode100.title70;

import java.util.Deque;
import java.util.LinkedList;

/**
 * 题目:最小栈
 * 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
 * 实现 MinStack 类:
 * MinStack() 初始化堆栈对象。
 * void push(int val) 将元素val推入堆栈。
 * void pop() 删除堆栈顶部的元素。
 * int top() 获取堆栈顶部的元素。
 * int getMin() 获取堆栈中的最小元素。
 * 
 * 示例 1:
 * 输入:
 * ["MinStack","push","push","push","getMin","pop","top","getMin"]
 * [[],[-2],[0],[-3],[],[],[],[]]
 * 输出:
 * [null,null,null,null,-3,null,0,-2]
 */
public class MinStack {
    /**
     * 解题思路:
     * 1. 使用辅助栈存储最小值
     * 2. 辅助栈与主栈同步操作,辅助栈顶始终是当前主栈中的最小值
     */
    private Deque<Integer> xStack;
    private Deque<Integer> minStack;

    public MinStack() {
        xStack = new LinkedList<>();
        minStack = new LinkedList<>();
        minStack.push(Integer.MAX_VALUE);
    }

    public void push(int val) {
        xStack.push(val);
        minStack.push(Math.min(minStack.peek(), val));
    }

    public void pop() {
        xStack.pop();
        minStack.pop();
    }

    public int top() {
        return xStack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }

    public static void main(String[] args) {
        MinStack minStack = new MinStack();
        minStack.push(-2);
        minStack.push(0);
        minStack.push(-3);
        System.out.println("Min: " + minStack.getMin()); // -3
        minStack.pop();
        System.out.println("Top: " + minStack.top());    // 0
        System.out.println("Min: " + minStack.getMin()); // -2
    }
}

title71 - 字符串解码

DecodeString.java

java
package leecode100.title71;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 题目:字符串解码
 * 给定一个经过编码的字符串,返回它解码后的字符串。
 * 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。
 * 注意 k 保证为正整数。
 * 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,
 * 且输入的方括号总是符合格式要求的。
 * 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,
 * 例如不会出现像 3a 或 2[4] 的输入。
 * 
 * 示例 1:
 * 输入: s = "3[a]2[bc]"
 * 输出: "aaabcbc"
 * 
 * 示例 2:
 * 输入: s = "3[a2[c]]"
 * 输出: "accaccacc"
 * 
 * 示例 3:
 * 输入: s = "2[abc]3[cd]ef"
 * 输出: "abcabccdcdcdef"
 * 
 * 示例 4:
 * 输入: s = "abc3[cd]xyz"
 * 输出: "abccdcdcdxyz"
 * 
 * 提示:
 * 1 <= s.length <= 30
 * s 由小写英文字母、数字和方括号 '[]' 组成
 * s 保证是一个有效的输入
 * s 中所有整数的取值范围为 [1, 300]
 */
public class DecodeString {
    /**
     * 解题思路:
     * 1. 使用两个栈分别存储数字和字符串
     * 2. 遇到数字时,解析完整的数字(可能多位)
     * 3. 遇到'['时,将当前数字和字符串入栈,并重置
     * 4. 遇到']'时,从栈中取出数字和字符串,进行重复操作
     * 5. 遇到普通字符时,直接添加到当前字符串中
     */

    public static void main(String[] args) {
        // Test case 1
        DecodeString solution = new DecodeString();
        String s1 = "3[a]2[bc]";
        String result1 = solution.decodeString(s1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        String s2 = "3[a2[c]]";
        String result2 = solution.decodeString(s2);
        System.out.println("Test case 2 result: " + result2);

        // Test case 3
        String s3 = "2[abc]3[cd]ef";
        String result3 = solution.decodeString(s3);
        System.out.println("Test case 3 result: " + result3);

        // Test case 4
        String s4 = "abc3[cd]xyz";
        String result4 = solution.decodeString(s4);
        System.out.println("Test case 4 result: " + result4);
    }

    public String decodeString(String s) {
        Deque<Integer> cntStack = new ArrayDeque<>();
        Deque<StringBuilder> strStack = new ArrayDeque<>();

        strStack.push(new StringBuilder());
        int num = 0;

        for (char ch : s.toCharArray()) {
            if (Character.isDigit(ch)) {
                num = num * 10 + (ch - '0');
            } else if (ch == '[') {
                cntStack.push(num);
                strStack.push(new StringBuilder());
                num = 0;
            } else if (ch == ']') {
                int cnt = cntStack.pop();
                String str = strStack.pop().toString();
                StringBuilder current = strStack.peek();
                while (cnt-- > 0) {
                    current.append(str);
                }
            } else {
                strStack.peek().append(ch);
            }
        }

        return strStack.peek().toString();
    }
}

title72 - 每日温度

DailyTemperatures.java

java
package leecode100.title72;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;

/**
 * 题目:每日温度
 * 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,
 * 其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。
 * 如果气温在这之后都不会升高,请在该位置用 0 来代替。
 * 
 * 示例 1:
 * 输入: temperatures = [73,74,75,71,69,72,76,73]
 * 输出: [1,1,4,2,1,1,0,0]
 * 
 * 示例 2:
 * 输入: temperatures = [30,40,50,60]
 * 输出: [1,1,1,0]
 * 
 * 示例 3:
 * 输入: temperatures = [30,60,90]
 * 输出: [1,1,0]
 * 
 * 提示:
 * 1 <= temperatures.length <= 10^5
 * 30 <= temperatures[i] <= 100
 */
public class DailyTemperatures {
    /**
     * 解题思路:
     * 1. 单调栈问题
     * 2. 使用单调递减栈来存储温度的索引
     * 3. 遍历温度数组,当当前温度大于栈顶索引对应的温度时,
     *    说明找到了栈顶索引的下一个更高温度
     * 4. 计算天数差并更新结果数组
     * 5. 将当前索引入栈,继续处理
     */

    public static void main(String[] args) {
        // Test case 1
        DailyTemperatures solution = new DailyTemperatures();
        int[] temperatures1 = {73,74,75,71,69,72,76,73};
        int[] result1 = solution.dailyTemperatures(temperatures1);
        System.out.println("Test case 1 result: " + Arrays.toString(result1));

        // Test case 2
        int[] temperatures2 = {30,40,50,60};
        int[] result2 = solution.dailyTemperatures(temperatures2);
        System.out.println("Test case 2 result: " + Arrays.toString(result2));

        // Test case 3
        int[] temperatures3 = {30,60,90};
        int[] result3 = solution.dailyTemperatures(temperatures3);
        System.out.println("Test case 3 result: " + Arrays.toString(result3));
    }

    public int[] dailyTemperatures(int[] temperatures) {
        int length = temperatures.length;
        Deque<Integer> stack = new ArrayDeque<>();
        int[] res = new int[length];

        for (int i = 0; i < length; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                int preIndex = stack.pop();
                res[preIndex] = i - preIndex;
            }
            stack.push(i);
        }

        return res;
    }
}

title73 - 柱状图中最大的矩形

LargestRectangleInHistogram.java

java
package leecode100.title73;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;

/**
 * 题目:柱状图中最大的矩形
 * 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
 * 求在该柱状图中,能够勾勒出来的矩形的最大面积。
 * 
 * 示例 1:
 * 输入:heights = [2,1,5,6,2,3]
 * 输出:10
 * 解释:最大的矩形为图中红色区域,面积为 10
 * 
 * 示例 2:
 * 输入:heights = [2,4]
 * 输出:4
 * 
 * 提示:
 * 1 <= heights.length <= 10^5
 * 0 <= heights[i] <= 10^4
 */
public class LargestRectangleInHistogram {
    /**
     * 解题思路:
     * 1. 单调栈问题
     * 2. 对于每个柱子,我们需要找到它左边和右边第一个比它矮的柱子
     * 3. 使用单调递增栈来处理
     * 4. 当遇到一个比栈顶元素矮的柱子时,说明找到了栈顶元素右边第一个较矮的柱子
     * 5. 此时可以计算以栈顶元素为高度的矩形面积
     * 6. 矩形的宽度是右边较矮柱子与左边较矮柱子之间的距离
     */

    public static void main(String[] args) {
        // Test case 1
        LargestRectangleInHistogram solution = new LargestRectangleInHistogram();
        int[] heights1 = {2,1,5,6,2,3};
        int result1 = solution.largestRectangleArea(heights1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        int[] heights2 = {2,4};
        int result2 = solution.largestRectangleArea(heights2);
        System.out.println("Test case 2 result: " + result2);
    }

    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];   // left[i]表示柱子i左边第一个比它矮的柱子索引
        int[] right = new int[n];  // right[i]表示柱子i右边第一个比它矮的柱子索引
        Arrays.fill(right, n);
        
        Deque<Integer> stack = new ArrayDeque<>();
        
        // 确定每个柱子左边第一个较矮的柱子
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
                // 当前柱子是栈顶元素右边第一个较矮的柱子
                right[stack.pop()] = i;
            }
            // 栈顶元素就是当前柱子左边第一个较矮的柱子
            left[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(i);
        }
        
        // 计算最大面积
        int maxArea = 0;
        for (int i = 0; i < n; i++) {
            // 面积 = 高度 * 宽度
            // 宽度 = 右边较矮柱子索引 - 左边较矮柱子索引 - 1
            int area = heights[i] * (right[i] - left[i] - 1);
            maxArea = Math.max(maxArea, area);
        }
        
        return maxArea;
    }
}

title74 - 数组中第K个最大元素

KthLargestElementInAnArray.java

java
package leecode100.title74;

/**
 * 题目:数组中第K个最大元素
 * 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
 * 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
 * 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
 * 
 * 示例 1:
 * 输入: [3,2,1,5,6,4], k = 2
 * 输出: 5
 * 
 * 示例 2:
 * 输入: [3,2,3,1,2,4,5,5,6], k = 4
 * 输出: 4
 * 
 * 提示:
 * 1 <= k <= nums.length <= 10^5
 * -10^4 <= nums[i] <= 10^4
 */
public class KthLargestElementInAnArray {
    /**
     * 解题思路:
     * 1. 使用堆排序的思想解决这个问题
     * 2. 构建一个最大堆
     * 3. 执行k-1次堆排序操作,每次将堆顶最大元素移到末尾
     * 4. 第k次操作后,堆顶元素就是第k大的元素
     * 5. buildMaxHeap方法用于构建初始的最大堆
     * 6. maxHeapify方法用于维护堆的性质
     */

    public static void main(String[] args) {
        // Test case 1
        KthLargestElementInAnArray solution = new KthLargestElementInAnArray();
        int[] nums1 = {3,2,1,5,6,4};
        int k1 = 2;
        int result1 = solution.findKthLargest(nums1, k1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        int[] nums2 = {3,2,3,1,2,4,5,5,6};
        int k2 = 4;
        int result2 = solution.findKthLargest(nums2, k2);
        System.out.println("Test case 2 result: " + result2);
    }

    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        // 构建最大堆
        for (int i = len / 2 - 1; i >= 0; i--) {
            maxHeapify(nums, i, len);
        }
        // 执行k-1次
        int cnt = k-1;
        int end = nums.length - 1;

        while ( cnt-- > 0 ) {
            swap(nums, 0, end);     // 将最大元素移到末尾
            len--;
            maxHeapify(nums, 0, len);  // 重新调整堆
            end--;
        }
        return nums[0];  // 堆顶即为第K大元素
    }

    private void maxHeapify(int[] nums, int i, int len) {
        int l = i * 2 + 1;    // 左子节点
        int r = i * 2 + 2;    // 右子节点
        int largest = i;      // 假设当前节点最大

        if (l < len && nums[l] > nums[largest]) {
            largest = l;
        }
        if (r < len && nums[r] > nums[largest]) {
            largest = r;
        }

        if (largest != i) {
            swap(nums, i, largest);
            maxHeapify(nums, largest, len);
        }
    }

    private void swap(int[] nums, int i, int largest) {
        int temp = nums[i];
        nums[i] = nums[largest];
        nums[largest] = temp;
    }
}

title75 - 前K个高频元素

TopKFrequentElements.java

java
package leecode100.title75;

import java.util.*;

/**
 * 题目:前K个高频元素
 * 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。
 * 你可以按 任意顺序 返回答案。
 * 
 * 示例 1:
 * 输入: nums = [1,1,1,2,2,3], k = 2
 * 输出: [1,2]
 * 
 * 示例 2:
 * 输入: nums = [1], k = 1
 * 输出: [1]
 * 
 * 提示:
 * 1 <= nums.length <= 10^5
 * k 的取值范围是 [1, 数组中不相同的元素的个数]
 * 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
 * 
 * 进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
 */
public class TopKFrequentElements {
    /**
     * 解题思路:
     * 1. 首先使用HashMap统计每个元素的频率
     * 2. 将元素和频率的对应关系存储在List中
     * 3. 使用堆排序的思想,构建最大堆(按频率排序)
     * 4. 执行k次堆排序操作,每次获取频率最高的元素
     * 5. 将获取的元素存储在结果数组中
     */

    public static void main(String[] args) {
        // Test case 1
        TopKFrequentElements solution = new TopKFrequentElements();
        int[] nums1 = {1,1,1,2,2,3};
        int k1 = 2;
        int[] result1 = solution.topKFrequent(nums1, k1);
        System.out.println("Test case 1 result: " + Arrays.toString(result1));

        // Test case 2
        int[] nums2 = {1};
        int k2 = 1;
        int[] result2 = solution.topKFrequent(nums2, k2);
        System.out.println("Test case 2 result: " + Arrays.toString(result2));
    }

    public int[] topKFrequent(int[] nums, int k) {
        // 统计频率
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        List<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet());

        // 构建最大堆(按频率排序)
        buildMaxHeap(entries, entries.size());

        int[] res = new int[k];
        int end = entries.size() - 1;
        for (int i = 0; i < k; i++) {
            // 获取频率最高的元素
            res[i] = entries.get(0).getKey();
            // 交换并调整堆
            swap(entries, 0, end);
            maxHeapify(entries, 0, end);
            end--;
        }

        return res;
    }

    private void buildMaxHeap(List<Map.Entry<Integer, Integer>> entries, int size) {
        for (int i = size / 2 - 1; i >= 0; i--) {
            maxHeapify(entries, i, size);
        }
    }

    private void maxHeapify(List<Map.Entry<Integer, Integer>> entries, int i, int size) {
        int l = i * 2 + 1;
        int r = i * 2 + 2;
        int largest = i;
        if (l < size && entries.get(l).getValue() > entries.get(largest).getValue()) {
            largest = l;
        }
        if (r < size && entries.get(r).getValue() > entries.get(largest).getValue()) {
            largest = r;
        }
        if (largest != i) {
            swap(entries, i, largest);
            maxHeapify(entries, largest, size);
        }
    }

    private void swap(List<Map.Entry<Integer, Integer>> entries, int i, int largest) {
        Map.Entry<Integer, Integer> temp = entries.get(i);
        entries.set(i, entries.get(largest));
        entries.set(largest, temp);
    }
}

title76 - 数据流的中位数

FindMedianFromDataStream.java

java
package leecode100.title76;

import java.util.PriorityQueue;

/**
 * 题目:数据流的中位数
 * 中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
 * 例如:
 * [2,3,4] 的中位数是 3
 * [2,3] 的中位数是 (2 + 3) / 2 = 2.5
 * 设计一个支持以下两种操作的数据结构:
 * void addNum(int num) - 从数据流中添加一个整数到数据结构中。
 * double findMedian() - 返回目前所有元素的中位数。
 * 
 * 示例 1:
 * 输入:
 * ["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
 * [[],[1],[2],[],[3],[]]
 * 输出:[null,null,null,1.50000,null,2.00000]
 * 
 * 示例 2:
 * 输入:
 * ["MedianFinder","addNum","findMedian","addNum","findMedian"]
 * [[],[2],[],[3],[]]
 * 输出:[null,null,2.00000,null,2.50000]
 * 
 * 提示:
 * 最多会对 addNum、findMedian 进行 50000 次调用。
 */
public class FindMedianFromDataStream {
    /**
     * 解题思路:
     * 1. 使用两个堆来维护数据流的中位数
     * 2. 一个最大堆存储较小的一半元素,一个最小堆存储较大的一半元素
     * 3. 保持两个堆的大小平衡,最大堆的大小等于最小堆的大小或比最小堆大1
     * 4. 添加元素时,根据元素大小决定放入哪个堆,并调整堆的平衡
     * 5. 查找中位数时,如果两个堆大小相等,返回两个堆顶元素的平均值;
     *    如果最大堆比最小堆大1,返回最大堆的堆顶元素
     */

    public static void main(String[] args) {
        // Test case 1
        MedianFinder medianFinder = new MedianFinder();
        medianFinder.addNum(1);
        medianFinder.addNum(2);
        System.out.printf("Median after adding 1,2: %.5f%n", medianFinder.findMedian()); // 1.50000
        medianFinder.addNum(3);
        System.out.printf("Median after adding 3: %.5f%n", medianFinder.findMedian()); // 2.00000
    }

    static class MedianFinder {
        private PriorityQueue<Integer> minHeap; // 存储较大的一半元素(最小堆)
        private PriorityQueue<Integer> maxHeap; // 存储较小的一半元素(最大堆)

        public MedianFinder() {
            minHeap = new PriorityQueue<>(); // 最小堆
            maxHeap = new PriorityQueue<>((x, y) -> y - x); // 最大堆
        }

        public void addNum(int num) {
            if (minHeap.isEmpty() || num > minHeap.peek()) {
                minHeap.offer(num);
            } else {
                maxHeap.offer(num);
            }

            // 平衡两个堆的大小
            if (minHeap.size() > maxHeap.size() + 1) {
                maxHeap.offer(minHeap.poll());
            } else if (maxHeap.size() > minHeap.size()) {
                minHeap.offer(maxHeap.poll());
            }
        }

        public double findMedian() {
            if (minHeap.size() == maxHeap.size()) {
                // 两个堆大小相等,返回两个堆顶元素的平均值
                return minHeap.peek() * 0.5 + maxHeap.peek() * 0.5;
            } else {
                // 最小堆比最大堆多一个元素,返回最小堆的堆顶元素
                return minHeap.peek();
            }
        }
    }
}

title77 - 买卖股票的最佳时机

BestTimeToBuyAndSellStock.java

java
package leecode100.title77;

/**
 * 题目:买卖股票的最佳时机
 * 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
 * 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。
 * 设计一个算法来计算你所能获取的最大利润。
 * 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
 * 
 * 示例 1:
 * 输入: [7,1,5,3,6,4]
 * 输出: 5
 * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
 * 
 * 示例 2:
 * 输入: [7,6,4,3,1]
 * 输出: 0
 * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
 * 
 * 提示:
 * 1 <= prices.length <= 10^5
 * 0 <= prices[i] <= 10^4
 */
public class BestTimeToBuyAndSellStock {
    /*
     * 解题思路:
     * 1. 遍历价格数组,记录到目前为止的最低价格
     * 2. 对于每一天,计算当天卖出能获得的最大利润(当天价格 - 最低价格)
     * 3. 更新全局最大利润
     * 4. 时间复杂度 O(n),空间复杂度 O(1)
     */

    public static void main(String[] args) {
        // Test case 1
        BestTimeToBuyAndSellStock solution = new BestTimeToBuyAndSellStock();
        int[] prices1 = {7,1,5,3,6,4};
        int result1 = solution.maxProfit(prices1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        int[] prices2 = {7,6,4,3,1};
        int result2 = solution.maxProfit(prices2);
        System.out.println("Test case 2 result: " + result2);
    }

    public int maxProfit(int[] prices) {
        int n = prices.length;
        int from = 0;
        int maxProfit = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            if (prices[i] < prices[from]) {
                from = i;
            }
            maxProfit = Math.max(maxProfit, prices[i] - prices[from]);
        }

        return Math.max(maxProfit, 0);
    }
}

title78 - 跳跃游戏

JumpGame.java

java
package leecode100.title78;

/**
 * 题目:跳跃游戏
 * 给定一个非负整数数组 nums ,你最初位于数组的第一个下标。
 * 数组中的每个元素代表你在该位置可以跳跃的最大长度。
 * 判断你是否能够到达最后一个下标。
 * 
 * 示例 1:
 * 输入: nums = [2,3,1,1,4]
 * 输出: true
 * 解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
 * 
 * 示例 2:
 * 输入: nums = [3,2,1,0,4]
 * 输出: false
 * 解释: 无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 ,所以永远不可能到达最后一个下标。
 * 
 * 提示:
 * 1 <= nums.length <= 10^4
 * 0 <= nums[i] <= 10^5
 */
public class JumpGame {
    /**
     * 解题思路:
     * 1. 使用贪心算法解决这个问题
     * 2. 维护一个变量maxReach表示当前能到达的最远下标
     * 3. 遍历数组,对于每个位置i:
     *    - 如果i > maxReach,说明无法到达位置i,返回false
     *    - 更新maxReach为max(maxReach, i + nums[i])
     *    - 如果maxReach >= nums.length - 1,说明可以到达最后一个下标,返回true
     * 4. 遍历完成后返回true
     */

    public static void main(String[] args) {
        // Test case 1
        JumpGame solution = new JumpGame();
        int[] nums1 = {2,3,1,1,4};
        boolean result1 = solution.canJump(nums1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        int[] nums2 = {3,2,1,0,4};
        boolean result2 = solution.canJump(nums2);
        System.out.println("Test case 2 result: " + result2);
    }

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

title79 - 跳跃游戏 II

JumpGameII.java

java
package leecode100.title79;

/**
 * 题目:跳跃游戏 II
 * 给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
 * 数组中的每个元素代表你在该位置可以跳跃的最大长度。
 * 你的目标是使用最少的跳跃次数到达数组的最后一个位置。
 * 假设你总是可以到达数组的最后一个位置。
 * 
 * 示例 1:
 * 输入: nums = [2,3,1,1,4]
 * 输出: 2
 * 解释: 跳到最后一个位置的最小跳跃数是 2。
 * 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
 * 
 * 示例 2:
 * 输入: nums = [2,3,0,1,4]
 * 输出: 2
 * 
 * 提示:
 * 1 <= nums.length <= 10^4
 * 0 <= nums[i] <= 1000
 */
public class JumpGameII {
    /**
     * 解题思路:
     * 1. 使用贪心算法解决这个问题
     * 2. 维护三个变量:
     *    - end: 当前跳跃范围的边界
     *    - maxPosition: 在当前跳跃范围内能到达的最远位置
     *    - steps: 跳跃次数
     * 3. 遍历数组(除最后一个元素外),对于每个位置i:
     *    - 更新maxPosition为max(maxPosition, i + nums[i])
     *    - 如果i == end,说明需要进行下一次跳跃:
     *      * 更新end为maxPosition
     *      * 增加steps
     * 4. 返回steps
     */

    public static void main(String[] args) {
        // Test case 1
        JumpGameII solution = new JumpGameII();
        int[] nums1 = {2,3,1,1,4};
        int result1 = solution.jump(nums1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        int[] nums2 = {2,3,0,1,4};
        int result2 = solution.jump(nums2);
        System.out.println("Test case 2 result: " + result2);
    }

    public int jump(int[] nums) {
        int end = 0;
        int maxPosition = 0;
        int steps = 0;
        for (int i = 0; i < nums.length - 1; i++) {
            maxPosition = Math.max(maxPosition, nums[i] + i);
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }

        return steps;
    }
}

title80 - 划分字母区间

PartitionLabels.java

java
package leecode100.title80;

import java.util.ArrayList;
import java.util.List;

/**
 * 题目:划分字母区间
 * 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,
 * 同一字母最多出现在一个片段中。结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 S 。
 * 返回一个表示每个字符串片段的长度的列表。
 * 
 * 示例 1:
 * 输入: s = "ababcbacadefegdehijhklij"
 * 输出: [9,7,8]
 * 解释: 划分结果为 "ababcbaca", "defegde", "hijhklij"。
 * 
 * 示例 2:
 * 输入: s = "eccbbbbdec"
 * 输出: [10]
 * 
 * 提示:
 * 1 <= s.length <= 500
 * s 仅由小写英文字母组成
 */
public class PartitionLabels {
    /**
     * 解题思路:
     * 1. 首先遍历字符串,记录每个字符最后出现的位置
     * 2. 再次遍历字符串,维护当前片段的结束位置maxEnd
     * 3. 对于每个字符,更新maxEnd为max(maxEnd, 该字符最后出现的位置)
     * 4. 当当前位置i等于maxEnd时,说明当前片段结束,记录长度并更新下一个片段的起始位置
     * 5. 时间复杂度O(n),空间复杂度O(1)
     */

    public static void main(String[] args) {
        // Test case 1
        PartitionLabels solution = new PartitionLabels();
        String s1 = "ababcbacadefegdehijhklij";
        List<Integer> result1 = solution.partitionLabels(s1);
        System.out.println("Test case 1 result: " + result1);

        // Test case 2
        String s2 = "eccbbbbdec";
        List<Integer> result2 = solution.partitionLabels(s2);
        System.out.println("Test case 2 result: " + result2);
    }

    public List<Integer> partitionLabels(String s) {
       int[] last = new int[26];
       int n = s.length();
        for (int i = 0; i < n; i++) {
            last[s.charAt(i) - 'a'] = i;
        }
        List<Integer> res = new ArrayList<>();
        int maxEnd = 0;
        int start = 0;
        for (int i = 0; i < n; i++) {
            maxEnd = Math.max(maxEnd, last[s.charAt(i) - 'a']);
            if (i == maxEnd) {
                res.add(i - start + 1);
                start = i + 1;
            }
        }

        return res;
    }
}