LeetCode1341~1360题目解析

LeetCode 1341-1360. 题解合集

文章目录


1341. Movie Rating(电影评分)

题目类型:数据库(SQL)

题目描述

表:Movies

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| movie_id      | int     |
| title         | varchar |
+---------------+---------+
movie_id 是这个表的主键(具有唯一值的列)。
title 是电影的名字。
每部电影都有一个唯一的 title。

表:Users

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user_id       | int     |
| name          | varchar |
+---------------+---------+
user_id 是表的主键(具有唯一值的列)。
'name' 列具有唯一值。

表:MovieRating

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| movie_id      | int     |
| user_id       | int     |
| rating        | int     |
| created_at    | date    |
+---------------+---------+
(movie_id, user_id) 是这个表的主键(具有唯一值的列的组合)。
这个表包含用户在其评论中对电影的评分 rating 。
created_at 是用户的点评日期。

请你编写一个解决方案:

复制代码
- 查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。

- 查找在 `February 2020`** 平均评分最高** 的电影名称。如果出现平局,返回字典序较小的电影名称。

字典序 ,即按字母在字典中出现顺序对字符串排序,字典序较小则意味着排序靠前。

返回结果格式如下例所示。

示例 1:

复制代码
**输入:**
Movies 表:
+-------------+--------------+
| movie_id    |  title       |
+-------------+--------------+
| 1           | Avengers     |
| 2           | Frozen 2     |
| 3           | Joker        |
+-------------+--------------+
Users 表:
+-------------+--------------+
| user_id     |  name        |
+-------------+--------------+
| 1           | Daniel       |
| 2           | Monica       |
| 3           | Maria        |
| 4           | James        |
+-------------+--------------+
MovieRating 表:
+-------------+--------------+--------------+-------------+
| movie_id    | user_id      | rating       | created_at  |
+-------------+--------------+--------------+-------------+
| 1           | 1            | 3            | 2020-01-12  |
| 1           | 2            | 4            | 2020-02-11  |
| 1           | 3            | 2            | 2020-02-12  |
| 1           | 4            | 1            | 2020-01-01  |
| 2           | 1            | 5            | 2020-02-17  |
| 2           | 2            | 2            | 2020-02-01  |
| 2           | 3            | 2            | 2020-03-01  |
| 3           | 1            | 3            | 2020-02-22  |
| 3           | 2            | 4            | 2020-02-25  |
+-------------+--------------+--------------+-------------+
**输出:**
Result 表:
+--------------+
| results      |
+--------------+
| Daniel       |
| Frozen 2     |
+--------------+
**解释:**
Daniel 和 Monica 都点评了 3 部电影("Avengers", "Frozen 2" 和 "Joker") 但是 Daniel 字典序比较小。
Frozen 2 和 Joker 在 2 月的评分都是 3.5,但是 Frozen 2 的字典序比较小。

解题思路

思考

两问结果竖着拼成一列:评分数最多的用户名(并列取字典序最小),以及 2020 2020 2020 年 2 2 2 月均分最高的电影名(并列同样取最小)。分别 J O I N \mathrm{JOIN} JOIN 后 G R O U P B Y \mathrm{GROUP\ BY} GROUP BY、排序并 L I M I T   1 \mathrm{LIMIT}\,1 LIMIT1,再用 U N I O N A L L \mathrm{UNION\ ALL} UNION ALL 上下拼接。

考察UNION + 分组排序 LIMIT:分别求评分最高与评分人数最多的电影,注意同票按字典序取最小。

代码实现

sql 复制代码
# Write your MySQL query statement below
(
    SELECT name AS results
    FROM
        Users
        JOIN MovieRating USING (user_id)
    GROUP BY user_id
    ORDER BY COUNT(1) DESC, name
    LIMIT 1
)
UNION ALL
(
    SELECT title
    FROM
        MovieRating
        JOIN Movies USING (movie_id)
    WHERE DATE_FORMAT(created_at, '%Y-%m') = '2020-02'
    GROUP BY movie_id
    ORDER BY AVG(rating) DESC, title
    LIMIT 1
);

复杂度分析

本题为数据库查询题,不涉及传统算法的时间/空间复杂度,执行效率由数据库优化器决定;建议对查询涉及的过滤列与连接列建立索引以加速。


1342. Number of Steps to Reduce a Number to Zero(将数字变成 0 的操作次数)

题目描述

给你一个非负整数 num,请你返回将它变成 0 所需要的步数。

如果当前数字是偶数,你需要把它除以 2 ;否则,减去 1。

示例 1

复制代码
num = 14 -> 6
  • 解释:14/2=7, 7-1=6, 6/2=3, 3-1=2, 2/2=1, 1-1=0(共 6 步)

示例 2

复制代码
num = 8 -> 4(8/2=4, 4/2=2, 2/2=1, 1-1=0)

示例 3

复制代码
num = 123 -> 12

约束:

  • 0 <= num <= 10^6

解题思路

解法一:模拟(主提交版本)

循环直到 num == 0:偶数除以 2,奇数减 1,每步计数 +1。

复杂度:时间 O(log num),空间 O(1)。

解法二:位运算优化(对照)

对二进制表示而言,偶数除 2 即右移一位,奇数减 1 即把最低位 1 置 0。

等价步数 = (最高位位数 - 1)+ 二进制中 1 的个数(每出现一个 1 需一次减 1,

最后一位 1 减完后为 0 不再除)。复杂度:时间 O(log num),空间 O(1)。

代码实现

java 复制代码
public class Solution {

    public int numberOfSteps(int num) {
        int steps = 0;
        while (num > 0) {
            if ((num & 1) == 0) {
                num >>= 1;
            } else {
                num -= 1;
            }
            steps++;
        }
        return steps;
    }

    public int numberOfStepsBits(int num) {
        if (num == 0) {
            return 0;
        }
        int steps = 0;
        int x = num;
        // 最高位所在位数 - 1 次的右移(每高一位多一次除法)
        int bits = 0;
        while (x > 0) {
            bits++;
            x >>= 1;
        }
        steps += bits - 1;
        // 二进制中 1 的个数
        int ones = Integer.bitCount(num);
        steps += ones;
        return steps;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.numberOfSteps(14));       // 6
        System.out.println(s.numberOfStepsBits(14));   // 6
        System.out.println(s.numberOfSteps(8));        // 4
        System.out.println(s.numberOfStepsBits(8));    // 4
        System.out.println(s.numberOfSteps(123));      // 12
        System.out.println(s.numberOfStepsBits(123));  // 12
        System.out.println(s.numberOfSteps(0));        // 0
        System.out.println(s.numberOfStepsBits(0));    // 0
    }
}

复杂度分析

复杂度:时间 O(log num),空间 O(1)。

最后一位 1 减完后为 0 不再除)。复杂度:时间 O(log num),空间 O(1)。


1343. Number of Sub-arrays of Size K and Average Greater than or Equal to Threshold(大小为 K 且平均值大于等于阈值的子数组数目)

题目描述

给你一个整数数组 arr 和两个整数 k 和 threshold。

请你返回长度为 k 且平均值大于等于 threshold 的子数组数目。

示例 1

复制代码
arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4 -> 3
  • 解释:子数组 2,5,5、5,5,5 和 5,5,8 的平均值分别为 4、5 和 6。

示例 2

复制代码
arr = [11,13,17,23,29,31,7,5,2,3], k = 3, threshold = 5 -> 6

约束:

  • 1 <= arr.length <= 10^5;1 <= k <= arr.length;0 <= threshold <= 10^4;
  • 0 <= arr[i] <= 10^4

解题思路

解法一:固定窗口前缀和(主提交版本)

维护长度为 k 的滑动窗口和:先算前 k 个元素和,再每次右移一位更新窗口和,

判断 和 >= k * threshold 即计数。

复杂度:时间 O(n),空间 O(1)。

解法二:前缀和数组(对照)

预处理前缀和 prefixi = sum(arr0...i-1),对每个起点 i 用

prefixi+k - prefixi 求窗口和并判断。复杂度:时间 O(n),空间 O(n)。

代码实现

java 复制代码
public class Solution {

    public int numOfSubarrays(int[] arr, int k, int threshold) {
        int n = arr.length;
        int sum = 0;
        for (int i = 0; i < k; i++) {
            sum += arr[i];
        }
        int limit = k * threshold;
        int ans = 0;
        if (sum >= limit) {
            ans++;
        }
        for (int i = k; i < n; i++) {
            sum += arr[i] - arr[i - k];
            if (sum >= limit) {
                ans++;
            }
        }
        return ans;
    }

    public int numOfSubarraysPrefix(int[] arr, int k, int threshold) {
        int n = arr.length;
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] + arr[i];
        }
        int limit = k * threshold;
        int ans = 0;
        for (int i = 0; i + k <= n; i++) {
            if (prefix[i + k] - prefix[i] >= limit) {
                ans++;
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.numOfSubarrays(new int[]{2, 2, 2, 2, 5, 5, 5, 8}, 3, 4));  // 3
        System.out.println(s.numOfSubarraysPrefix(new int[]{2, 2, 2, 2, 5, 5, 5, 8}, 3, 4)); // 3
        System.out.println(s.numOfSubarrays(new int[]{11, 13, 17, 23, 29, 31, 7, 5, 2, 3}, 3, 5)); // 6
        System.out.println(s.numOfSubarraysPrefix(new int[]{11, 13, 17, 23, 29, 31, 7, 5, 2, 3}, 3, 5)); // 6
        System.out.println(s.numOfSubarrays(new int[]{1, 1, 1, 1, 1}, 1, 0)); // 5
        System.out.println(s.numOfSubarraysPrefix(new int[]{1, 1, 1, 1, 1}, 1, 0)); // 5
    }
}

复杂度分析

复杂度:时间 O(n),空间 O(1)。

prefixi+k - prefixi 求窗口和并判断。复杂度:时间 O(n),空间 O(n)。


1344. Angle Between Hands of a Clock(时钟指针的夹角)

题目描述

给你两个数 hour 和 minutes。请你返回在时钟上,由给定时间的时针和分针组成的较小角的角度(60 单位制)。

示例 1

复制代码
hour = 12, minutes = 30 -> 165

示例 2

复制代码
hour = 3, minutes = 30 -> 75

示例 3

复制代码
hour = 3, minutes = 15 -> 7.5

示例 4

复制代码
hour = 4, minutes = 50 -> 155

示例 5

复制代码
hour = 12, minutes = 0 -> 0

约束:

  • 1 <= hour <= 12;0 <= minutes <= 59;与标准答案误差在 10^-5 以内都被视为正确

解题思路

解法一:角度公式(主提交版本)

分针每分钟走 6°,从 12 点起分针角 = minutes * 6。

时针每小时走 30°,每分钟走 0.5°,从 12 点起时针角 = hour % 12 * 30 + minutes * 0.5。

夹角 = |时针角 - 分针角|,与 360 取余后返回 min(angle, 360-angle)。

复杂度:时间 O(1),空间 O(1)。

解法二:双指针钟面模运算(对照)

把钟面看成 360°,角度统一归一到 [0, 360),求两角差的较小者。

写法上借助 (a - b + 360) % 360 与 360 - 该值取 min。

复杂度:时间 O(1),空间 O(1)。

代码实现

java 复制代码
public class Solution {

    public double angleClock(int hour, int minutes) {
        double minuteAngle = minutes * 6.0;
        double hourAngle = (hour % 12) * 30.0 + minutes * 0.5;
        double diff = Math.abs(hourAngle - minuteAngle);
        return Math.min(diff, 360.0 - diff);
    }

    public double angleClockMod(int hour, int minutes) {
        double minuteAngle = minutes * 6.0;
        double hourAngle = (hour % 12) * 30.0 + minutes * 0.5;
        double diff = Math.abs(hourAngle - minuteAngle) % 360.0;
        return Math.min(diff, 360.0 - diff);
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.angleClock(12, 30));    // 165.0
        System.out.println(s.angleClockMod(12, 30)); // 165.0
        System.out.println(s.angleClock(3, 30));     // 75.0
        System.out.println(s.angleClockMod(3, 30));  // 75.0
        System.out.println(s.angleClock(3, 15));     // 7.5
        System.out.println(s.angleClockMod(3, 15));  // 7.5
        System.out.println(s.angleClock(4, 50));     // 155.0
        System.out.println(s.angleClockMod(4, 50));  // 155.0
        System.out.println(s.angleClock(12, 0));     // 0.0
        System.out.println(s.angleClockMod(12, 0));  // 0.0
    }
}

复杂度分析

复杂度:时间 O(1),空间 O(1)。


1345. Jump Game IV(跳跃游戏 IV)

题目描述

给你一个整数数组 arr,你一开始在数组的第一个元素处(下标为 0)。

每一步,你可以从下标 i 跳到下标 i + 1、i - 1 或者 j:

  • i + 1 需满足:i + 1 < arr.length
  • i - 1 需满足:i - 1 >= 0
  • j 需满足:arr[i] == arr[j] 且 i != j
    请你返回到达数组最后一个元素的下标处所需的最少操作次数。
    注意:任何时候你都不能跳到数组外面。

示例 1

复制代码
arr = [100,-23,-23,404,100,23,23,23,3,404] -> 3

示例 2

复制代码
arr = [7] -> 0

示例 3

复制代码
arr = [7,6,9,6,9,6,9,7] -> 1

约束:

  • 1 <= arr.length <= 5 * 104;-108 <= arr[i] <= 10^8

解题思路

解法一:BFS + 等值组一次扩展(主提交版本)

从 0 出发 BFS 求到 n-1 的最短步数。关键剪枝:用 Map<Integer, List> 记录

每个值出现的所有下标,贪心策略------跳跃到「等值下标」时,第一次到达某值就遍历该值全部下标

入队,之后该值不再用(因为 BFS 首次覆盖就是最短,重复扩展无意义)。

每个下标最多入队一次、等值组最多被扫一次,复杂度 O(n)。

解法二:双向 BFS(对照)

从 0 和 n-1 同时 BFS,任一侧扩展时与另一侧集合相交即得最短步数;

同样配合「等值组一次消费」剪枝。复杂度 O(n)。

代码实现

java 复制代码
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {

    public int minJumps(int[] arr) {
        int n = arr.length;
        if (n == 1) {
            return 0;
        }
        // 值 -> 下标列表
        Map<Integer, List<Integer>> pos = new HashMap<>();
        for (int i = 0; i < n; i++) {
            pos.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
        }
        boolean[] visited = new boolean[n];
        visited[0] = true;
        Deque<Integer> queue = new ArrayDeque<>();
        queue.offer(0);
        int steps = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int t = 0; t < size; t++) {
                int i = queue.poll();
                if (i == n - 1) {
                    return steps;
                }
                // i+1
                if (i + 1 < n && !visited[i + 1]) {
                    visited[i + 1] = true;
                    queue.offer(i + 1);
                }
                // i-1
                if (i - 1 >= 0 && !visited[i - 1]) {
                    visited[i - 1] = true;
                    queue.offer(i - 1);
                }
                // 同值下标(首次遇到该值才扩展)
                List<Integer> same = pos.remove(arr[i]);
                if (same != null) {
                    for (int j : same) {
                        if (!visited[j]) {
                            visited[j] = true;
                            queue.offer(j);
                        }
                    }
                }
            }
            steps++;
        }
        return -1;
    }

    public int minJumpsTwoWay(int[] arr) {
        int n = arr.length;
        if (n == 1) {
            return 0;
        }
        Map<Integer, List<Integer>> pos = new HashMap<>();
        for (int i = 0; i < n; i++) {
            pos.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
        }
        int[] distS = new int[n];
        int[] distT = new int[n];
        java.util.Arrays.fill(distS, -1);
        java.util.Arrays.fill(distT, -1);
        distS[0] = 0;
        distT[n - 1] = 0;
        Deque<Integer> qS = new ArrayDeque<>();
        Deque<Integer> qT = new ArrayDeque<>();
        qS.offer(0);
        qT.offer(n - 1);
        while (!qS.isEmpty() && !qT.isEmpty()) {
            if (qS.size() <= qT.size()) {
                int ans = expandTwo(qS, distS, distT, arr, pos);
                if (ans != -1) {
                    return ans;
                }
            } else {
                int ans = expandTwo(qT, distT, distS, arr, pos);
                if (ans != -1) {
                    return ans;
                }
            }
        }
        return -1;
    }

    private int expandTwo(Deque<Integer> queue, int[] dist, int[] other, int[] arr,
                          Map<Integer, List<Integer>> pos) {
        int size = queue.size();
        for (int t = 0; t < size; t++) {
            int i = queue.poll();
            int d = dist[i];
            int[] nxt = {i + 1, i - 1};
            for (int ni : nxt) {
                if (ni >= 0 && ni < arr.length) {
                    if (other[ni] != -1) {
                        return d + 1 + other[ni];
                    }
                    if (dist[ni] == -1) {
                        dist[ni] = d + 1;
                        queue.offer(ni);
                    }
                }
            }
            List<Integer> list = pos.remove(arr[i]); // 该值整体只消费一次
            if (list != null) {
                for (int j : list) {
                    if (other[j] != -1) {
                        return d + 1 + other[j];
                    }
                    if (dist[j] == -1) {
                        dist[j] = d + 1;
                        queue.offer(j);
                    }
                }
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.minJumps(new int[]{100, -23, -23, 404, 100, 23, 23, 23, 3, 404})); // 3
        System.out.println(s.minJumpsTwoWay(new int[]{100, -23, -23, 404, 100, 23, 23, 23, 3, 404})); // 3
        System.out.println(s.minJumps(new int[]{7})); // 0
        System.out.println(s.minJumpsTwoWay(new int[]{7})); // 0
        System.out.println(s.minJumps(new int[]{7, 6, 9, 6, 9, 6, 9, 7})); // 1
        System.out.println(s.minJumpsTwoWay(new int[]{7, 6, 9, 6, 9, 6, 9, 7})); // 1
        System.out.println(s.minJumps(new int[]{11, 22, 7, 7, 7, 7, 7, 7, 7, 22, 13})); // 3
        System.out.println(s.minJumpsTwoWay(new int[]{11, 22, 7, 7, 7, 7, 7, 7, 7, 22, 13})); // 3
    }
}

复杂度分析


1346. Check If N and Its Double Exist(检查整数及其两倍数是否存在)

题目描述

给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即 N = 2 * M)。

更正式地,检查是否存在两个下标 i 和 j 满足:

  • i != j
  • 0 <= i, j < arr.length
  • arr[i] == 2 * arr[j]

示例 1

复制代码
arr = [10,2,5,3] -> true(N = 10 是 M = 5 的两倍)

示例 2

复制代码
arr = [7,1,14,11] -> true(N = 14 是 M = 7 的两倍)

示例 3

复制代码
arr = [3,1,7,11] -> false

约束:

  • 2 <= arr.length <= 500;-10^3 <= arr[i] <= 10^3

解题思路

解法一:哈希集合一次遍历(主提交版本)

遍历数组,对每个 x 检查集合中是否已有 2x 或 x/2(x 为偶数时)。

注意 x 与 2x 需要不同下标,但由于边遍历边插入,当前 x 尚未入集合,天然避免同下标。

复杂度:时间 O(n),空间 O(n)。

解法二:两重循环暴力(对照)

枚举所有 i != j 的下标对检查 arri == 2*arrj。

复杂度:时间 O(n^2),空间 O(1)。

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class Solution {

    public boolean checkIfExist(int[] arr) {
        Set<Integer> seen = new HashSet<>();
        for (int x : arr) {
            if (seen.contains(x * 2)) {
                return true;
            }
            if (x % 2 == 0 && seen.contains(x / 2)) {
                return true;
            }
            seen.add(x);
        }
        return false;
    }

    public boolean checkIfExistBrute(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (i != j && arr[i] == 2 * arr[j]) {
                    return true;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.checkIfExist(new int[]{10, 2, 5, 3}));      // true
        System.out.println(s.checkIfExistBrute(new int[]{10, 2, 5, 3})); // true
        System.out.println(s.checkIfExist(new int[]{7, 1, 14, 11}));     // true
        System.out.println(s.checkIfExistBrute(new int[]{7, 1, 14, 11}));// true
        System.out.println(s.checkIfExist(new int[]{3, 1, 7, 11}));      // false
        System.out.println(s.checkIfExistBrute(new int[]{3, 1, 7, 11})); // false
        System.out.println(s.checkIfExist(new int[]{-2, 0, 10, -19, 4, 6, -8})); // false
        System.out.println(s.checkIfExist(new int[]{0, 0}));             // true(0 是 0 的两倍)
        System.out.println(s.checkIfExistBrute(new int[]{0, 0}));        // true
        System.out.println(s.checkIfExist(new int[]{0, 1}));             // false
        System.out.println(s.checkIfExistBrute(new int[]{0, 1}));        // false
    }
}

复杂度分析

复杂度:时间 O(n),空间 O(n)。

复杂度:时间 O(n^2),空间 O(1)。


1347. Minimum Number of Steps to Make Two Strings Anagram(制造字母异位词的最小步骤数)

题目描述

给你两个长度相等的字符串 s 和 t。每一个步骤中,你可以选择将 t 中的任一字符替换为另一个字符。

返回使 t 成为 s 的字母异位词的最小步骤数。

字母异位词指字母相同,但排列不同(也可能相同)的字符串。

示例 1

复制代码
s = "bab", t = "aba" -> 1(用 'b' 替换 t 中的第一个 'a')

示例 2

复制代码
s = "leetcode", t = "practice" -> 5

示例 3

复制代码
s = "anagram", t = "mangaar" -> 0

约束:

  • 1 <= s.length <= 50000;s.length == t.length;s 和 t 只包含小写英文字母

解题思路

解法一:频次差值(主提交版本)

统计 s 与 t 各字符出现次数,t 中「多余」的字符即为需替换数:

对每个字符求 max(0, cntTc - cntSc) 之和,或等价于

s 中 t 缺少的字符数(Σ max(0, cntS - cntT))。

复杂度:时间 O(n),空间 O(1)(26 数组)。

解法二:单数组差分(对照)

只用一个 26 数组:扫 s 时 +1,扫 t 时 -1;最后统计正数之和即需替换次数。

复杂度:时间 O(n),空间 O(1)。

代码实现

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

public class Solution {

    public int minSteps(String s, String t) {
        int[] cnt = new int[26];
        for (char c : s.toCharArray()) {
            cnt[c - 'a']++;
        }
        for (char c : t.toCharArray()) {
            cnt[c - 'a']--;
        }
        int ans = 0;
        for (int v : cnt) {
            if (v > 0) {
                ans += v; // s 中有而 t 中缺少的字符数 = 需要替换的次数
            }
        }
        return ans;
    }

    public int minStepsTwoArrays(String s, String t) {
        int[] cs = new int[26];
        int[] ct = new int[26];
        for (char c : s.toCharArray()) {
            cs[c - 'a']++;
        }
        for (char c : t.toCharArray()) {
            ct[c - 'a']++;
        }
        int ans = 0;
        for (int i = 0; i < 26; i++) {
            ans += Math.max(0, ct[i] - cs[i]);
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.minSteps("bab", "aba"));            // 1
        System.out.println(s.minStepsTwoArrays("bab", "aba"));   // 1
        System.out.println(s.minSteps("leetcode", "practice"));  // 5
        System.out.println(s.minStepsTwoArrays("leetcode", "practice")); // 5
        System.out.println(s.minSteps("anagram", "mangaar"));    // 0
        System.out.println(s.minStepsTwoArrays("anagram", "mangaar")); // 0
        System.out.println(s.minSteps("a", "b"));                // 1
        System.out.println(s.minStepsTwoArrays("a", "b"));       // 1
    }
}

复杂度分析

复杂度:时间 O(n),空间 O(1)(26 数组)。

复杂度:时间 O(n),空间 O(1)。


1348. Tweet Counts Per Frequency(推文计数)

题目描述

一家社交媒体公司正试图通过分析特定时间段内出现的推文数量来监控其网站上的活动。

这些时间段可以根据特定的频率(每分钟、每小时或每一天)划分为更小的时间段。

实现 TweetCounts 类:

  • TweetCounts() 初始化 TweetCounts 对象。
  • void recordTweet(String tweetName, int time) 存储推文发布记录。
  • List<Integer> getTweetCountsPerFrequency(String freq, String tweetName, int startTime, int endTime)
    返回一个整数列表,表示在给定时间 `startTime`, `endTime`(单位秒)内、按给定频率划分的每个
    时间块中带有 tweetName 的 tweet 的数量。
    频率:freq 为 "minute"/"hour"/"day",块大小分别为 60/3600/86400 秒。
    分块规则:起点为 startTime,每个块长度 delta,最后一个块以 endTime 结束
    (块 k 区间为 `startTime` + `k`\*delta, `min`(`startTime` + (`k`+1)\*delta - 1, `endTime`))。

示例

复制代码
recordTweet("tweet3",0); recordTweet("tweet3",60); recordTweet("tweet3",10);
getTweetCountsPerFrequency("minute","tweet3",0,59)  -> [2]
getTweetCountsPerFrequency("minute","tweet3",0,60)  -> [2,1]
recordTweet("tweet3",120);
getTweetCountsPerFrequency("hour","tweet3",0,210)   -> [4]

约束:

  • 0 <= time, startTime, endTime <= 10^9;0 <= endTime - startTime <= 10^4

解题思路

解法一:哈希表 + 线性统计(主提交版本)

Map<name, List> 记录每个用户所有推文时间,查询时对每一条时间落在 startTime, endTime

的推文计算块号 idx = (t - startTime) / delta 并累加;结果长度 = numBlocks。

复杂度:查询 O(T)(T 为该用户推文总数),空间 O(总推文数)。

解法二:TreeMap 有序集合 + 二分(对照)

Map<name, TreeMap<Integer, Integer>> 维护有序推文时间,每个块用 subMap 二分统计块内计数,

避免遍历全部推文。复杂度:查询 O(numBlocks * log T),空间 O(总推文数)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Solution {

    public static class TweetCounts {

        private final Map<String, List<Integer>> tweets;

        public TweetCounts() {
            tweets = new HashMap<>();
        }

        public void recordTweet(String tweetName, int time) {
            tweets.computeIfAbsent(tweetName, k -> new ArrayList<>()).add(time);
        }

        public List<Integer> getTweetCountsPerFrequency(String freq, String tweetName,
                                                        int startTime, int endTime) {
            int delta = freq.equals("minute") ? 60 : (freq.equals("hour") ? 3600 : 86400);
            int numBlocks = (endTime - startTime) / delta + 1;
            int[] ans = new int[numBlocks];
            List<Integer> list = tweets.getOrDefault(tweetName, new ArrayList<>());
            for (int t : list) {
                if (t < startTime || t > endTime) {
                    continue;
                }
                int idx = (t - startTime) / delta;
                ans[idx]++;
            }
            List<Integer> res = new ArrayList<>(numBlocks);
            for (int v : ans) {
                res.add(v);
            }
            return res;
        }
    }

    public static class TweetCountsTree {

        private final Map<String, TreeMap<Integer, Integer>> map;

        public TweetCountsTree() {
            map = new HashMap<>();
        }

        public void recordTweet(String tweetName, int time) {
            map.computeIfAbsent(tweetName, k -> new TreeMap<>())
                    .merge(time, 1, Integer::sum);
        }

        public List<Integer> getTweetCountsPerFrequency(String freq, String tweetName,
                                                        int startTime, int endTime) {
            int delta = freq.equals("minute") ? 60 : (freq.equals("hour") ? 3600 : 86400);
            int numBlocks = (endTime - startTime) / delta + 1;
            List<Integer> res = new ArrayList<>(numBlocks);
            TreeMap<Integer, Integer> tm = map.getOrDefault(tweetName, new TreeMap<>());
            for (int k = 0; k < numBlocks; k++) {
                long lo = startTime + (long) k * delta;
                long hi = Math.min((long) startTime + (long) (k + 1) * delta - 1, endTime);
                int cnt = 0;
                for (int v : tm.subMap((int) lo, true, (int) hi, true).values()) {
                    cnt += v;
                }
                res.add(cnt);
            }
            return res;
        }
    }

    public static void main(String[] args) {
        TweetCounts tc = new TweetCounts();
        tc.recordTweet("tweet3", 0);
        tc.recordTweet("tweet3", 60);
        tc.recordTweet("tweet3", 10);
        System.out.println(tc.getTweetCountsPerFrequency("minute", "tweet3", 0, 59)); // [2]
        System.out.println(tc.getTweetCountsPerFrequency("minute", "tweet3", 0, 60)); // [2,1]
        tc.recordTweet("tweet3", 120);
        System.out.println(tc.getTweetCountsPerFrequency("hour", "tweet3", 0, 210));  // [4]

        TweetCountsTree tct = new TweetCountsTree();
        tct.recordTweet("tweet3", 0);
        tct.recordTweet("tweet3", 60);
        tct.recordTweet("tweet3", 10);
        System.out.println(tct.getTweetCountsPerFrequency("minute", "tweet3", 0, 59)); // [2]
        System.out.println(tct.getTweetCountsPerFrequency("minute", "tweet3", 0, 60)); // [2,1]
        tct.recordTweet("tweet3", 120);
        System.out.println(tct.getTweetCountsPerFrequency("hour", "tweet3", 0, 210));  // [4]
    }
}

复杂度分析

复杂度:查询 O(T)(T 为该用户推文总数),空间 O(总推文数)。

避免遍历全部推文。复杂度:查询 O(numBlocks * log T),空间 O(总推文数)。


1349. Maximum Students Taking Exam(参加考试的最大学生数)

题目描述

给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 '#' 表示;

否则,用 '.' 表示。

学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面

或者后面的学生的答卷。请你计算并返回该考场可以容纳的同时参加考试且无法作弊的最大学生人数。

学生必须坐在状况良好的座位上。

示例 1

复制代码
seats = [["#",".","#","#",".","#"],[".","#","#","#","#","."],
["#",".","#","#",".","#"]] -> 4

示例 2

复制代码
seats = [[".","#"],["#","#"],["#","."],["#","#"],[".","#"]] -> 3

示例 3

复制代码
seats = [[".",".",".",".","#"],[".","#",".","#","."],[".",".","#",".","."],
[".","#",".","#","."],["#",".",".",".","#"]] -> 10

约束:

  • seats 只包含字符 '.' 和 '#';m == seats.length;n == seatsi.length;
  • 1 <= m <= 8;1 <= n <= 8

解题思路

解法一:状态压缩 DP(主提交版本)

每行用一个 n 位二进制掩码表示该行哪些位置坐了学生(位 1 = 坐人)。

dpimask = 前 i 行(0 基)且第 i 行坐法为 mask 时的最大学生数。

转移条件(mask 需合法):

  • 无相邻:mask & (mask >> 1) == 0;
  • 只坐好座位:(mask & brokeni) == 0,brokeni 为第 i 行坏座位掩码;
  • 与上一行 mask2 不冲突:无左上冲突 (mask2 << 1) & mask == 0,
    无右上冲突 (mask2 >> 1) & mask == 0。
    dpimask = max(dpi-1mask2) + popcount(mask)。
    复杂度:时间 O(m * 4^n)(n<=8 可过),空间 O(m * 2^n)。
    解法二:DFS 逐行逐位回溯(对照)
    递归处理行 r 与当前行掩码 curMask,逐列决策该位是否放人(检查左、左上、右上、是否好座位),
    递归加记忆化 memormask 加速。逻辑等价于 DP,写法偏搜索。
    复杂度:时间 O(m * 2^n * n),空间 O(m * 2^n)。

代码实现

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

public class Solution {

    public int maxStudents(char[][] seats) {
        int m = seats.length, n = seats[0].length;
        int[] broken = new int[m]; // 坏座位掩码
        for (int i = 0; i < m; i++) {
            int mask = 0;
            for (int j = 0; j < n; j++) {
                if (seats[i][j] == '#') {
                    mask |= (1 << j);
                }
            }
            broken[i] = mask;
        }
        int states = 1 << n;
        int[][] dp = new int[m][states];
        for (int[] row : dp) {
            Arrays.fill(row, -1);
        }
        int ans = 0;
        for (int mask = 0; mask < states; mask++) {
            if (valid(mask, n) && (mask & broken[0]) == 0) {
                dp[0][mask] = Integer.bitCount(mask);
                ans = Math.max(ans, dp[0][mask]);
            }
        }
        for (int i = 1; i < m; i++) {
            for (int mask = 0; mask < states; mask++) {
                if (!valid(mask, n) || (mask & broken[i]) != 0) {
                    continue;
                }
                int best = 0;
                for (int prev = 0; prev < states; prev++) {
                    if (dp[i - 1][prev] == -1) {
                        continue;
                    }
                    // 左上方:prev 的位 1 若在 mask 的位置 j-1 有学生则冲突
                    // 右上方:prev 的位 1 若在 mask 的位置 j+1 有学生则冲突
                    if (((prev << 1) & mask) != 0) {
                        continue;
                    }
                    if (((prev >> 1) & mask) != 0) {
                        continue;
                    }
                    best = Math.max(best, dp[i - 1][prev]);
                }
                dp[i][mask] = best + Integer.bitCount(mask);
                ans = Math.max(ans, dp[i][mask]);
            }
        }
        return ans;
    }

    private boolean valid(int mask, int n) {
        // 无相邻 1
        return (mask & (mask >> 1)) == 0 && (mask & (mask << 1)) == 0;
    }

    public int maxStudentsDfs(char[][] seats) {
        int m = seats.length, n = seats[0].length;
        int[] broken = new int[m];
        for (int i = 0; i < m; i++) {
            int mask = 0;
            for (int j = 0; j < n; j++) {
                if (seats[i][j] == '#') {
                    mask |= (1 << j);
                }
            }
            broken[i] = mask;
        }
        int states = 1 << n;
        int[][] memo = new int[m][states];
        for (int[] row : memo) {
            Arrays.fill(row, -1);
        }
        int ans = 0;
        for (int mask = 0; mask < states; mask++) {
            if (valid(mask, n) && (mask & broken[0]) == 0) {
                memo[0][mask] = Integer.bitCount(mask);
                ans = Math.max(ans, memo[0][mask]);
            }
        }
        for (int i = 1; i < m; i++) {
            for (int mask = 0; mask < states; mask++) {
                if (valid(mask, n) && (mask & broken[i]) == 0) {
                    memo[i][mask] = Integer.bitCount(mask) + bestPrev(memo[i - 1], mask, n, states);
                    ans = Math.max(ans, memo[i][mask]);
                }
            }
        }
        return ans;
    }

    private int bestPrev(int[] prevDp, int mask, int n, int states) {
        int best = 0;
        for (int prev = 0; prev < states; prev++) {
            if (prevDp[prev] == -1) {
                continue;
            }
            if (((prev << 1) & mask) != 0 || ((prev >> 1) & mask) != 0) {
                continue;
            }
            best = Math.max(best, prevDp[prev]);
        }
        return best;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        char[][] seats1 = {
                {'#', '.', '#', '#', '.', '#'},
                {'.', '#', '#', '#', '#', '.'},
                {'#', '.', '#', '#', '.', '#'}};
        System.out.println(s.maxStudents(seats1));     // 4
        System.out.println(s.maxStudentsDfs(seats1));  // 4
        char[][] seats2 = {
                {'.', '#'},
                {'#', '#'},
                {'#', '.'},
                {'#', '#'},
                {'.', '#'}};
        System.out.println(s.maxStudents(seats2));     // 3
        System.out.println(s.maxStudentsDfs(seats2));  // 3
        char[][] seats3 = {
                {'.', '.', '.', '.', '#'},
                {'.', '#', '.', '#', '.'},
                {'.', '.', '#', '.', '.'},
                {'.', '#', '.', '#', '.'},
                {'#', '.', '.', '.', '#'}};
        System.out.println(s.maxStudents(seats3));     // 10
        System.out.println(s.maxStudentsDfs(seats3));  // 10
    }
}

复杂度分析

复杂度:时间 O(m * 4^n)(n<=8 可过),空间 O(m * 2^n)。

复杂度:时间 O(m * 2^n * n),空间 O(m * 2^n)。


1350. Students With Invalid Departments(院系无效的学生 🔒)

题目类型:数据库(SQL)

题目描述

院系表: Departments

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
+---------------+---------+
在 SQL 中,id 是该表的主键
该表包含一所大学每个院系的 id 信息

学生表: Students

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
| department_id | int     |
+---------------+---------+
在 SQL 中,id 是该表的主键
该表包含一所大学每个学生的 id 和他/她就读的院系信息

找出那些所在院系不存在的学生的 id 和姓名

可以以 任何顺序 返回结果。

下面是返回结果格式的例子。

示例 1:

复制代码
**输入:**
Departments 表:
+------+--------------------------+
| id   | name                     |
+------+--------------------------+
| 1    | Electrical Engineering   |
| 7    | Computer Engineering     |
| 13   | Bussiness Administration |
+------+--------------------------+
Students 表:
+------+----------+---------------+
| id   | name     | department_id |
+------+----------+---------------+
| 23   | Alice    | 1             |
| 1    | Bob      | 7             |
| 5    | Jennifer | 13            |
| 2    | John     | 14            |
| 4    | Jasmine  | 77            |
| 3    | Steve    | 74            |
| 6    | Luis     | 1             |
| 8    | Jonathan | 7             |
| 7    | Daiana   | 33            |
| 11   | Madelynn | 1             |
+------+----------+---------------+
**输出:**
+------+----------+
| id   | name     |
+------+----------+
| 2    | John     |
| 7    | Daiana   |
| 4    | Jasmine  |
| 3    | Steve    |
+------+----------+
**解释:**
John, Daiana, Steve 和 Jasmine 所在的院系分别是 14, 33, 74 和 77, 其中 14, 33, 74 和 77 并不存在于院系表

解题思路

思考

找出 department_id \textit{department\_id} department_id 不在院系表中的学生。子查询列出全部院系编号,再用 N O T I N \mathrm{NOT\ IN} NOT IN 过滤学生即可。

考察LEFT JOIN 判空:学生表左连接院系表,院系为空即为无效学生。

代码实现

sql 复制代码
# Write your MySQL query statement below
SELECT id, name
FROM Students
WHERE department_id NOT IN (SELECT id FROM Departments);

复杂度分析

本题为数据库查询题,不涉及传统算法的时间/空间复杂度,执行效率由数据库优化器决定;建议对查询涉及的过滤列与连接列建立索引以加速。


1351. Count Negative Numbers in a Sorted Matrix(统计有序矩阵中的负数)

题目描述

给你一个 m * n 的矩阵 grid,矩阵中的元素无论是按行还是按列,都以非严格递减顺序排列。

请你统计并返回 grid 中负数的数目。

示例 1

复制代码
grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]] -> 8

示例 2

复制代码
grid = [[3,2],[1,0]] -> 0

约束:

  • 1 <= m, n <= 100;-100 <= grid[i][j] <= 100。

解题思路

解法一:右上角线性扫描(主提交版本)

从最后一列开始维护指针 c,表示当前行最右侧第一个非负元素之后的位置。

由于行、列都非严格递减,当遍历到第 r 行时,若 gridrc < 0 则左移 c;

该行负数个数 = n - 1 - c。c 单调不减,总体 O(m + n)。

解法二:逐行二分查找(对照)

对每一行用二分查找第一个负数所在下标 p(即第一个 < 0 的位置),

该行负数个数 = n - p,累加即可。复杂度 O(m * log n)。

代码实现

java 复制代码
public class Solution {

    public int countNegatives(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int count = 0;
        int c = n - 1;
        for (int r = 0; r < m; r++) {
            while (c >= 0 && grid[r][c] < 0) {
                c--;
            }
            count += n - 1 - c;
        }
        return count;
    }

    public int countNegativesBinary(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int count = 0;
        for (int r = 0; r < m; r++) {
            int lo = 0, hi = n; // 二分的区间为 [0, n],p 为第一个负数下标
            while (lo < hi) {
                int mid = (lo + hi) >>> 1;
                if (grid[r][mid] < 0) {
                    hi = mid;
                } else {
                    lo = mid + 1;
                }
            }
            count += n - lo;
        }
        return count;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.countNegatives(new int[][]{{4,3,2,-1},{3,2,1,-1},{1,1,-1,-2},{-1,-1,-2,-3}})); // 8
        System.out.println(s.countNegatives(new int[][]{{3,2},{1,0}})); // 0
        System.out.println(s.countNegatives(new int[][]{{-5}})); // 1
        System.out.println(s.countNegativesBinary(new int[][]{{4,3,2,-1},{3,2,1,-1},{1,1,-1,-2},{-1,-1,-2,-3}})); // 8
        System.out.println(s.countNegativesBinary(new int[][]{{3,2},{1,0}})); // 0
        System.out.println(s.countNegativesBinary(new int[][]{{-5}})); // 1
    }
}

复杂度分析


1352. Product of the Last K Numbers(最后 K 个数的乘积)

题目描述

设计一个算法,该算法接受一个整数流并检索该流中最后 k 个整数的乘积。

实现 ProductOfNumbers 类:

  • ProductOfNumbers() 用一个空的流初始化对象。
  • void add(int num) 将数字 num 添加到当前数字列表的最后面。
  • int getProduct(int k) 返回当前数字列表中,最后 k 个数字的乘积。
    你可以假设当前列表中始终至少包含 k 个数字。
    题目数据保证:任何时候,任一连续数字序列的乘积都在 32 位整数范围内,不会溢出。

示例

复制代码
["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"]
[[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]
  • 输出 :`null`,`null`,`null`,`null`,`null`,`null`,20,40,0,`null`,32
    解释见题面(0 参与乘法会直接命中前缀积的 0 哨兵)。

解题思路

解法一:前缀积 + 0 哨兵(主提交版本)

维护前缀积数组 prefixi = 前 i 个数的乘积,prefix0 = 1。

当 add(0) 时重置列表(置位哨兵 1),这样任何包含 0 的区间都会被探测到。

getProduct(k) = prefixn / prefixn - k,若区间含 0 则返回 0。

复杂度:add O(1),getProduct O(1),空间 O(流长度)。

解法二:朴素列表 + 末 k 重乘(对照)

直接保存所有数字,getProduct(k) 时对最后 k 个数字累乘。

复杂度:add O(1),getProduct O(k)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Solution {

    public static class ProductOfNumbers {

        private final List<Integer> prefix;
        private int lastZero; // 最近一次 add(0) 时的前缀长度(重置计数)

        public ProductOfNumbers() {
            prefix = new ArrayList<>();
            prefix.add(1); // prefix.get(i) = 前 i 个数的乘积
            lastZero = 0;
        }

        public void add(int num) {
            if (num == 0) {
                prefix.clear();
                prefix.add(1);
                lastZero = 0;
                return;
            }
            int last = prefix.get(prefix.size() - 1);
            prefix.add(last * num);
        }

        public int getProduct(int k) {
            int n = prefix.size() - 1;
            if (k > n) {
                return 0; // 区间内一定包含 0(0 已把前缀重置)
            }
            return prefix.get(n) / prefix.get(n - k);
        }
    }

    public static class ProductOfNumbersNaive {

        private final List<Integer> nums;

        public ProductOfNumbersNaive() {
            nums = new ArrayList<>();
        }

        public void add(int num) {
            nums.add(num);
        }

        public int getProduct(int k) {
            int res = 1;
            int n = nums.size();
            for (int i = n - k; i < n; i++) {
                res *= nums.get(i);
            }
            return res;
        }
    }

    public static void main(String[] args) {
        ProductOfNumbers p = new ProductOfNumbers();
        p.add(3);
        p.add(0);
        p.add(2);
        p.add(5);
        p.add(4);
        System.out.println(p.getProduct(2)); // 20
        System.out.println(p.getProduct(3)); // 40
        System.out.println(p.getProduct(4)); // 0
        p.add(8);
        System.out.println(p.getProduct(2)); // 32

        ProductOfNumbersNaive pn = new ProductOfNumbersNaive();
        pn.add(3);
        pn.add(0);
        pn.add(2);
        pn.add(5);
        pn.add(4);
        System.out.println(pn.getProduct(2)); // 20
        System.out.println(pn.getProduct(3)); // 40
        System.out.println(pn.getProduct(4)); // 0
        pn.add(8);
        System.out.println(pn.getProduct(2)); // 32
    }
}

复杂度分析

复杂度:add O(1),getProduct O(1),空间 O(流长度)。

复杂度:add O(1),getProduct O(k)。


1353. Maximum Number of Events That Can Be Attended(最多可以参加的会议数目)

题目描述

给你一个数组 events,其中 eventsi = `startDayi`, `endDayi`,表示会议 i 开始于 startDayi,

结束于 endDayi。你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i。

在任意一天 d 中只能参加一场会议。请你返回你可以参加的最大会议数目。

示例 1

复制代码
events = [[1,2],[2,3],[3,4]] -> 3

示例 2

复制代码
events = [[1,2],[2,3],[3,4],[1,2]] -> 4

约束:

  • 1 <= events.length <= 10^5;eventsi.length == 2;1 <= startDayi <= endDayi <= 10^5。

解题思路

解法一:排序 + 贪心 + 优先队列(主提交版本)

按开始时间排序事件,并维护一个小根堆存放「已开始未参加」会议的 (结束时间)。

用指针 day 从 1 扫描到最大结束时间:把 day 当天开始的事件入堆,弹出并丢弃已过期的;

若堆非空则参加堆中结束最早的会议(弹出一次记一题)。复杂度 O(E * log E + D * log E)。

解法二:按结束时间排序 + 布尔占位(对照)

按 endDayi 升序、startDayi 升序排序,对每个会议在 start, end 内找第一个未占用的天。

能占就占。复杂度 O(E * maxGap * D),仅作正确性对照。

代码实现

java 复制代码
import java.util.Arrays;
import java.util.PriorityQueue;

public class Solution {

    public int maxEvents(int[][] events) {
        Arrays.sort(events, (a, b) -> a[0] - b[0]);
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        int idx = 0;
        int n = events.length;
        int maxDay = 0;
        for (int[] e : events) {
            maxDay = Math.max(maxDay, e[1]);
        }
        int res = 0;
        for (int day = 1; day <= maxDay; day++) {
            while (idx < n && events[idx][0] == day) {
                pq.offer(events[idx][1]);
                idx++;
            }
            while (!pq.isEmpty() && pq.peek() < day) {
                pq.poll(); // 已过期
            }
            if (!pq.isEmpty()) {
                pq.poll();
                res++;
            }
        }
        return res;
    }

    public int maxEventsPlaceholder(int[][] events) {
        Arrays.sort(events, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[0] - b[0]);
        int maxDay = 0;
        for (int[] e : events) {
            maxDay = Math.max(maxDay, e[1]);
        }
        boolean[] used = new boolean[maxDay + 2];
        int res = 0;
        for (int[] e : events) {
            for (int d = e[0]; d <= e[1]; d++) {
                if (!used[d]) {
                    used[d] = true;
                    res++;
                    break;
                }
            }
        }
        return res;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.maxEvents(new int[][]{{1,2},{2,3},{3,4}})); // 3
        System.out.println(s.maxEvents(new int[][]{{1,2},{2,3},{3,4},{1,2}})); // 4
        System.out.println(s.maxEvents(new int[][]{{1,1},{1,1},{1,1}})); // 1
        System.out.println(s.maxEventsPlaceholder(new int[][]{{1,2},{2,3},{3,4}})); // 3
        System.out.println(s.maxEventsPlaceholder(new int[][]{{1,2},{2,3},{3,4},{1,2}})); // 4
        System.out.println(s.maxEventsPlaceholder(new int[][]{{1,1},{1,1},{1,1}})); // 1
    }
}

复杂度分析


1354. Construct Target Array With Multiple Sums(多次求和构造目标数组)

题目描述

给你一个整数数组 target。一开始,你有一个数组 A,它的所有元素均为 1,你可以执行以下操作:

  • 令 x 为你数组里所有元素的和;
  • 选择满足 0 <= i < target.size 的任意下标 i,并让 A 数组里下标为 i 处的值为 x;
  • 你可以重复该过程任意次。
    如果能从 A 开始构造出目标数组 target,请你返回 True,否则返回 False。

示例 1

复制代码
target = [9,3,5] -> true([1,1,1] -> [1,3,1] -> [1,3,5] -> [9,3,5])

示例 2

复制代码
target = [1,1,1,2] -> false

示例 3

复制代码
target = [8,5] -> true

约束:

  • 1 <= target.length <= 5 * 10^4;1 <= target[i] <= 10^9。

解题思路

解法一:最大堆逆向模拟(主提交版本)

逆向构造:目标数组 sum 已知,最后一个替换的位置一定是当前最大元素 max 被「总和 - 其他元素」

替换回去,即 prevPlace = max - (sum - max)。由于替换只发生在最大元素上,我们把 max 弹出,

用 prevPlace 放回。若 prevPlace <= 0 说明不可能;若 prevPlace == 1 可直接返回 true。

复杂度:O(n * log(sum / max)),空间 O(n)。

解法二:朴素逐轮扫描最大值(对照)

每轮线性扫描出当前最大元素并尝试逆向还原,仅作为正确性对照。

复杂度:O(轮数 * n)。

代码实现

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

public class Solution {

    public boolean isPossible(int[] target) {
        if (target.length == 1) {
            return target[0] == 1;
        }
        long sum = 0;
        PriorityQueue<Long> pq = new PriorityQueue<>((a, b) -> Long.compare(b, a));
        for (int v : target) {
            sum += v;
            pq.offer((long) v);
        }
        while (true) {
            long max = pq.poll();
            long rest = sum - max;
            if (max == 1) {
                return true; // 全部还原为 1
            }
            if (rest == 0) {
                return false;
            }
            // 还原前一轮该位置的值:max - rest * t,t = (max - nxt)/rest
            long prev = max % rest;
            if (prev == 0) {
                prev = rest; // max 能被 rest 整除且 > rest 时,还原值为 rest
            }
            if (prev <= 0) {
                return false;
            }
            if (prev == max) {
                return false; // 未发生有效缩小
            }
            sum = sum - max + prev;
            pq.offer(prev);
        }
    }

    public boolean isPossibleLinear(int[] target) {
        int n = target.length;
        if (n == 1) {
            return target[0] == 1;
        }
        long sum = 0;
        for (int v : target) {
            sum += v;
        }
        while (true) {
            int maxIdx = -1;
            long max = -1;
            for (int i = 0; i < n; i++) {
                if (target[i] > max) {
                    max = target[i];
                    maxIdx = i;
                }
            }
            if (max == 1) {
                return true;
            }
            long rest = sum - max;
            if (rest == 0) {
                return false;
            }
            long prev = max % rest;
            if (prev == 0) {
                prev = rest;
            }
            if (prev <= 0 || prev == max) {
                return false;
            }
            sum = sum - max + prev;
            target[maxIdx] = (int) prev;
        }
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.isPossible(new int[]{9, 3, 5})); // true
        System.out.println(s.isPossible(new int[]{1, 1, 1, 2})); // false
        System.out.println(s.isPossible(new int[]{8, 5})); // true
        System.out.println(s.isPossible(new int[]{1})); // true
        System.out.println(s.isPossible(new int[]{1, 1000000000})); // true
        System.out.println(s.isPossibleLinear(new int[]{9, 3, 5})); // true
        System.out.println(s.isPossibleLinear(new int[]{1, 1, 1, 2})); // false
        System.out.println(s.isPossibleLinear(new int[]{8, 5})); // true
    }
}

复杂度分析

复杂度:O(n * log(sum / max)),空间 O(n)。

复杂度:O(轮数 * n)。


1355. Activity Participants(活动参与者 🔒)

题目类型:数据库(SQL)

题目描述

表: Friends

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
| activity      | varchar |
+---------------+---------+
id 是朋友的 id,并且在 SQL 中,是该表的主键
name 是朋友的名字
activity 是朋友参加的活动的名字

表: Activities

复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
+---------------+---------+
在 SQL 中,id 是该表的主键
name 是活动的名字

找出那些既没有最多,也没有最少参与者的活动的名字。

Activities 表中的任意活动都有在 Friends 中参与过。

可以以 任何顺序 返回结果。

下面是返回结果格式的例子。

示例 1:

复制代码
**输入:**
Friends 表:
+------+--------------+---------------+
| id   | name         | activity      |
+------+--------------+---------------+
| 1    | Jonathan D.  | Eating        |
| 2    | Jade W.      | Singing       |
| 3    | Victor J.    | Singing       |
| 4    | Elvis Q.     | Eating        |
| 5    | Daniel A.    | Eating        |
| 6    | Bob B.       | Horse Riding  |
+------+--------------+---------------+
Activities 表:
+------+--------------+
| id   | name         |
+------+--------------+
| 1    | Eating       |
| 2    | Singing      |
| 3    | Horse Riding |
+------+--------------+
**输出:**
+--------------+
| activity     |
+--------------+
| Singing      |
+--------------+
**解释:**
Eating 活动有三个人参加, 是最多人参加的活动 (Jonathan D. , Elvis Q. and Daniel A.)
Horse Riding 活动有一个人参加, 是最少人参加的活动 (Bob B.)
Singing 活动有两个人参加 (Victor J. and Jade W.)

解题思路

思考

找出参加人数既不是最多也不是最少的活动。先按活动分组计数,再与全局最小、最大人数比较,留下严格介于两者之间的活动名。

考察窗口函数 + 过滤:按活动统计参与人数,再用窗口函数/自连接排除最多与最少(或按题意取非首尾)。

代码实现

sql 复制代码
# Write your MySQL query statement below
WITH
    t AS (
        SELECT activity, COUNT(1) AS cnt
        FROM Friends
        GROUP BY activity
    )
SELECT activity
FROM t
WHERE cnt > (SELECT MIN(cnt) FROM t) AND cnt < (SELECT MAX(cnt) FROM t);

复杂度分析

本题为数据库查询题,不涉及传统算法的时间/空间复杂度,执行效率由数据库优化器决定;建议对查询涉及的过滤列与连接列建立索引以加速。


1356. Sort Integers by The Number of 1 Bits(根据数字二进制下 1 的数目排序)

题目描述

给你一个整数数组 arr。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。

如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。

请你返回排序后的数组。

示例 1

复制代码
arr = [0,1,2,3,4,5,6,7,8] -> [0,1,2,4,8,3,5,6,7]

示例 2

复制代码
arr = [1024,512,256,128,64,32,16,8,4,2,1] -> [1,2,4,8,16,32,64,128,256,512,1024]

约束:

  • 1 <= arr.length <= 500;0 <= arr[i] <= 10^4

解题思路

解法一:自定义比较器(主提交版本)

用 Integer.bitCount 统计二进制 1 个数,按 (bitCount, 数值) 升序排序后装箱返回。

复杂度:时间 O(n log n),空间 O(n)。

解法二:计数桶排序(对照)

1 的个数范围 0, 14(arri <= 10^4 < 2^14),按 bitCount 分桶后每桶内数值升序拼接。

复杂度:时间 O(n log n)(桶内也需排序),空间 O(n)。

代码实现

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

public class Solution {

    public int[] sortByBits(int[] arr) {
        Integer[] boxed = new Integer[arr.length];
        for (int i = 0; i < arr.length; i++) {
            boxed[i] = arr[i];
        }
        Arrays.sort(boxed, (a, b) -> {
            int ca = Integer.bitCount(a), cb = Integer.bitCount(b);
            return ca != cb ? Integer.compare(ca, cb) : Integer.compare(a, b);
        });
        int[] ans = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            ans[i] = boxed[i];
        }
        return ans;
    }

    public int[] sortByBitsBucket(int[] arr) {
        int n = arr.length;
        java.util.List<Integer>[] buckets = new java.util.ArrayList[15];
        for (int i = 0; i < 15; i++) {
            buckets[i] = new java.util.ArrayList<>();
        }
        for (int x : arr) {
            buckets[Integer.bitCount(x)].add(x);
        }
        int[] ans = new int[n];
        int idx = 0;
        for (java.util.List<Integer> b : buckets) {
            b.sort(Integer::compareTo);
            for (int v : b) {
                ans[idx++] = v;
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(Arrays.toString(s.sortByBits(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}))); // [0,1,2,4,8,3,5,6,7]
        System.out.println(Arrays.toString(s.sortByBitsBucket(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8})));
        System.out.println(Arrays.toString(s.sortByBits(new int[]{1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1})));
        System.out.println(Arrays.toString(s.sortByBitsBucket(new int[]{1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1})));
    }
}

复杂度分析

复杂度:时间 O(n log n),空间 O(n)。

复杂度:时间 O(n log n)(桶内也需排序),空间 O(n)。


1357. Apply Discount Every n Orders(每隔 n 个顾客打折)

题目描述

超市里正在举行打折活动,每隔 n 个顾客会得到 discount 的折扣。

超市里有一些商品,第 i 种商品为 productsi 且每件单品的价格为 prices[i]。

结账系统会统计顾客的数目,每隔 n 个顾客结账时,该顾客的账单都会打折,折扣为 discount

(也就是如果原本账单为 x,那么实际金额会变成 x - (discount * x) / 100),然后系统会重新开始计数。

顾客会购买一些商品,producti 是顾客购买的第 i 种商品,amounti 是对应的购买该种商品的数目。

实现 Cashier 类:

  • Cashier(int n, int discount, int[] products, int[] prices)
  • double getBill(int[] product, int[] amount) 返回账单实际金额(有打折返回打折后结果),
    误差在 10^-5 以内视为正确。

示例

复制代码
Cashier(3, 50, {1,2,3,4,5,6,7}, {100,200,300,400,300,200,100})
getBill({1,2},{1,2})      -> 500.0
getBill({3,7},{10,10})    -> 4000.0
getBill({1,2,3,4,5,6,7},{1,1,1,1,1,1,1}) -> 800.0(每第 3 个顾客打 50% 折扣)

约束:

  • 1 <= n <= 10^4;0 <= discount <= 100;1 <= products.length <= 200;
  • 1 <= productsi, prices[i], amounti <= 1000。

解题思路

解法一:哈希表 + 计数折扣(主提交版本)

用 Map 记录 商品 id -> 价格,维护顾客计数器 customerCount;

每来一位顾客 customerCount++,若 customerCount % n == 0 则打折(金额 *= (100-discount)/100)

并自动复位计数(% n 天然完成)。复杂度:时间 O(m)(m = product.length),空间 O(len)。

解法二:显式复位计数(对照)

同一映射结构,用 if (count == n) count = 0 显式复位,折扣命中判断与金额计算拆开,

便于对照阅读。复杂度同解法一。

代码实现

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class Solution {

    public static class Cashier {

        private final int n;
        private final int discount;
        private final Map<Integer, Integer> priceMap;
        private int count;

        public Cashier(int n, int discount, int[] products, int[] prices) {
            this.n = n;
            this.discount = discount;
            priceMap = new HashMap<>();
            for (int i = 0; i < products.length; i++) {
                priceMap.put(products[i], prices[i]);
            }
            count = 0;
        }

        public double getBill(int[] product, int[] amount) {
            count++;
            double total = 0;
            for (int i = 0; i < product.length; i++) {
                total += priceMap.get(product[i]) * amount[i];
            }
            if (count % n == 0) {
                total = total - (discount * total) / 100.0;
            }
            return total;
        }
    }

    public static class CashierExplicit {

        private final int n;
        private final int discount;
        private final Map<Integer, Integer> priceMap;
        private int count;

        public CashierExplicit(int n, int discount, int[] products, int[] prices) {
            this.n = n;
            this.discount = discount;
            priceMap = new HashMap<>();
            for (int i = 0; i < products.length; i++) {
                priceMap.put(products[i], prices[i]);
            }
            count = 0;
        }

        public double getBill(int[] product, int[] amount) {
            count++;
            boolean hit = (count % n == 0);
            if (count == n) {
                count = 0; // 显式复位(与 %n 等价)
            }
            double total = 0;
            for (int i = 0; i < product.length; i++) {
                total += priceMap.get(product[i]) * amount[i];
            }
            if (hit) {
                total *= (100 - discount) / 100.0;
            }
            return total;
        }
    }

    public static void main(String[] args) {
        Cashier c = new Cashier(3, 50, new int[]{1, 2, 3, 4, 5, 6, 7},
                new int[]{100, 200, 300, 400, 300, 200, 100});
        System.out.println(c.getBill(new int[]{1, 2}, new int[]{1, 2}));       // 500.0
        System.out.println(c.getBill(new int[]{3, 7}, new int[]{10, 10}));     // 4000.0
        System.out.println(c.getBill(new int[]{1, 2, 3, 4, 5, 6, 7}, new int[]{1, 1, 1, 1, 1, 1, 1})); // 800.0(第 3 个顾客 50% 折扣)
        System.out.println(c.getBill(new int[]{4}, new int[]{10}));            // 4000.0
        System.out.println(c.getBill(new int[]{7, 3}, new int[]{10, 10}));     // 4000.0
        System.out.println(c.getBill(new int[]{7, 5, 3, 1, 6, 4, 2}, new int[]{10, 10, 10, 9, 9, 9, 7})); // 7350.0
        System.out.println(c.getBill(new int[]{2, 3, 5}, new int[]{5, 3, 2})); // 2500.0

        CashierExplicit ce = new CashierExplicit(3, 50, new int[]{1, 2, 3, 4, 5, 6, 7},
                new int[]{100, 200, 300, 400, 300, 200, 100});
        System.out.println(ce.getBill(new int[]{1, 2}, new int[]{1, 2}));       // 500.0
        System.out.println(ce.getBill(new int[]{3, 7}, new int[]{10, 10}));     // 4000.0
        System.out.println(ce.getBill(new int[]{1, 2, 3, 4, 5, 6, 7}, new int[]{1, 1, 1, 1, 1, 1, 1})); // 800.0
    }
}

复杂度分析

并自动复位计数(% n 天然完成)。复杂度:时间 O(m)(m = product.length),空间 O(len)。


1358. Number of Substrings Containing All Three Characters(包含所有三种字符的子字符串数目)

题目描述

给你一个字符串 s,它只包含三种字符 a, b 和 c。

请你返回 a,b 和 c 都至少出现过一次的子字符串数目。

示例 1

复制代码
s = "abcabc" -> 10

示例 2

复制代码
s = "aaacb" -> 3

示例 3

复制代码
s = "abc" -> 1

约束:

  • 3 <= s.length <= 5 * 10^4;s 只包含字符 'a'、'b' 或 'c'

解题思路

解法一:滑动窗口计数(主提交版本)

固定左边界 l,移动右边界 r 直到 l, r 内 a/b/c 均至少一个;

此时以 l 为起点、r...n-1 结尾的子串都满足条件,计数加 n - r,

然后 l 右移并更新窗口频次,循环。

复杂度:时间 O(n),空间 O(1)。

解法二:前缀后缀统计(对照)

对每个位置 r 作为右端点,找最小的 l 使 l, r 含全部三种字符;

用 lastPos 记录最近一次出现的 a/b/c 位置,min(last) 即该 l,

则满足条件的子串数为 min(lastA, lastB, lastC) + 1。

复杂度:时间 O(n),空间 O(1)。

代码实现

java 复制代码
public class Solution {

    public int numberOfSubstrings(String s) {
        int n = s.length();
        int[] cnt = new int[3];
        int ans = 0;
        int l = 0;
        for (int r = 0; r < n; r++) {
            cnt[s.charAt(r) - 'a']++;
            while (cnt[0] > 0 && cnt[1] > 0 && cnt[2] > 0) {
                // 以 l 为左端点的所有子串 [l, r]..[l, n-1] 均满足
                ans += n - r;
                cnt[s.charAt(l) - 'a']--;
                l++;
            }
        }
        return ans;
    }

    public int numberOfSubstringsLast(String s) {
        int n = s.length();
        int[] last = {-1, -1, -1};
        int ans = 0;
        for (int r = 0; r < n; r++) {
            last[s.charAt(r) - 'a'] = r;
            int minLast = Math.min(last[0], Math.min(last[1], last[2]));
            if (minLast != -1) {
                // 所有 l <= minLast 的 [l, r] 都含三种字符
                ans += minLast + 1;
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.numberOfSubstrings("abcabc"));       // 10
        System.out.println(s.numberOfSubstringsLast("abcabc"));   // 10
        System.out.println(s.numberOfSubstrings("aaacb"));        // 3
        System.out.println(s.numberOfSubstringsLast("aaacb"));    // 3
        System.out.println(s.numberOfSubstrings("abc"));          // 1
        System.out.println(s.numberOfSubstringsLast("abc"));      // 1
        System.out.println(s.numberOfSubstrings("aaaaaaaaaa"));   // 0
        System.out.println(s.numberOfSubstringsLast("aaaaaaaaaa"));// 0
    }
}

复杂度分析

复杂度:时间 O(n),空间 O(1)。


1359. Count All Valid Pickup and Delivery Options(有效的快递序列数目)

题目描述

给你 n 笔订单,每笔订单都需要快递服务。

计算所有有效的取货/交付可能的顺序,使 delivery(i) 总是在 pickup(i) 之后。

由于答案可能很大,请返回答案对 10^9 + 7 取余的结果。

示例 1

复制代码
n = 1 -> 1

示例 2

复制代码
n = 2 -> 6

示例 3

复制代码
n = 3 -> 90

约束:

  • 1 <= n <= 500

解题思路

解法一:组合插入 DP(主提交版本)

设 f(k) 为 k 笔订单的有效序列数。已有 k 笔订单的序列含 2k 个操作位。

插入第 k+1 笔订单时,其 pickup 可插入 2k+1 个空位;放入后 delivery 必须在 pickup 之后,

可选的相对位置为 其余 2k+1 个空位中位于 pickup 之后的个数(即剩余空位数)------

更严谨的推导:在 2k+1 个空位中插入 P,然后有 2k+2 个空位插入 D 需在 P 后,

均匀来看一半,所以增量 = (2k+1) * (2k+2) / 2 = (2k+1)(k+1)。

因此 f(k+1) = f(k) * (2k+1) * (k+1)。

初始化 f(1) = 1。复杂度:时间 O(n),空间 O(1)。

解法二:组合数学显式公式(对照)

f(n) = (2n)! / (2^n)。逐项递推避免除法取模:

f(k) = f(k-1) * (2k-1) * k(展开与解法一等价,注意乘法顺序)。

复杂度:时间 O(n),空间 O(1)。

代码实现

java 复制代码
public class Solution {

    public int countOrders(int n) {
        final long MOD = 1_000_000_007L;
        long ans = 1;
        for (int k = 2; k <= n; k++) {
            // 已有 k-1 笔订单:空位总数 2(k-1)+1 = 2k-1
            // 新订单 pick 插入任一空位,deliver 只能在其后一半位置
            long ways = (long) (2 * k - 1) * k % MOD;
            ans = ans * ways % MOD;
        }
        return (int) ans;
    }

    public int countOrdersAlt(int n) {
        final long MOD = 1_000_000_007L;
        long ans = 1;
        for (int k = 1; k <= n; k++) {
            ans = ans * (2 * k - 1) % MOD;
            ans = ans * k % MOD;
        }
        return (int) ans;
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.countOrders(1));      // 1
        System.out.println(s.countOrdersAlt(1));   // 1
        System.out.println(s.countOrders(2));      // 6
        System.out.println(s.countOrdersAlt(2));   // 6
        System.out.println(s.countOrders(3));      // 90
        System.out.println(s.countOrdersAlt(3));   // 90
        System.out.println(s.countOrders(4));      // 2520
        System.out.println(s.countOrdersAlt(4));   // 2520
        System.out.println(s.countOrders(500));    // 取模结果
        System.out.println(s.countOrdersAlt(500)); // 取模结果
    }
}

复杂度分析

初始化 f(1) = 1。复杂度:时间 O(n),空间 O(1)。

复杂度:时间 O(n),空间 O(1)。


1360. Number of Days Between Two Dates(日期之间隔几天)

题目描述

请你编写一个程序来计算两个日期之间隔了多少天。

日期以字符串形式给出,格式为 YYYY-MM-DD,如示例所示。

示例 1

复制代码
date1 = "2019-06-29", date2 = "2019-06-30" -> 1

示例 2

复制代码
date1 = "2020-01-15", date2 = "2019-12-31" -> 15

约束:

  • 给定的日期是 1971 年到 2100 年之间的有效日期

解题思路

解法一:天数换算 + Math.abs(主提交版本)

将每个日期换算为从基准年 1971-01-01 起的天数:

累加完整年份天数(闰年 366 平年 365)+ 当年已过月份天数 + 当月日数 - 1。

两日期天数差的绝对值即答案。复杂度:时间 O(1)(年份跨度 ≤ 130),空间 O(1)。

解法二:库函数 LocalDate(对照)

用 java.time.LocalDate 解析并计算 ChronoUnit.DAYS.between。

复杂度:时间 O(1),空间 O(1)。

代码实现

java 复制代码
public class Solution {

    public int daysBetweenDates(String date1, String date2) {
        int d1 = daysFromEpoch(date1);
        int d2 = daysFromEpoch(date2);
        return Math.abs(d1 - d2);
    }

    private int daysFromEpoch(String date) {
        int year = Integer.parseInt(date.substring(0, 4));
        int month = Integer.parseInt(date.substring(5, 7));
        int day = Integer.parseInt(date.substring(8, 10));
        int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        int total = 0;
        // 从 1971 年到 year 前一年的完整年
        for (int y = 1971; y < year; y++) {
            total += isLeap(y) ? 366 : 365;
        }
        // 当年已过的月份
        for (int m = 0; m < month - 1; m++) {
            total += daysInMonth[m];
        }
        // 闰年且已过二月,补一天
        if (isLeap(year) && month > 2) {
            total += 1;
        }
        total += day;
        return total;
    }

    private boolean isLeap(int year) {
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }

    public int daysBetweenDatesLib(String date1, String date2) {
        java.time.LocalDate d1 = java.time.LocalDate.parse(date1);
        java.time.LocalDate d2 = java.time.LocalDate.parse(date2);
        return (int) Math.abs(java.time.temporal.ChronoUnit.DAYS.between(d1, d2));
    }

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.daysBetweenDates("2019-06-29", "2019-06-30")); // 1
        System.out.println(s.daysBetweenDatesLib("2019-06-29", "2019-06-30")); // 1
        System.out.println(s.daysBetweenDates("2020-01-15", "2019-12-31")); // 15
        System.out.println(s.daysBetweenDatesLib("2020-01-15", "2019-12-31")); // 15
        System.out.println(s.daysBetweenDates("1971-01-01", "1972-01-01")); // 366(1972 闰年)
        System.out.println(s.daysBetweenDatesLib("1971-01-01", "1972-01-01")); // 366
        System.out.println(s.daysBetweenDates("2000-03-01", "2000-02-28")); // 2(2000 闰年)
        System.out.println(s.daysBetweenDatesLib("2000-03-01", "2000-02-28")); // 2
    }
}

复杂度分析

两日期天数差的绝对值即答案。复杂度:时间 O(1)(年份跨度 ≤ 130),空间 O(1)。

复杂度:时间 O(1),空间 O(1)。

相关推荐
QianhangQianping1 小时前
撤销重做不是「存快照」:一个可视化编辑器的Operation 设计
java·前端·数据库·typescript·编辑器·前端架构·撤销重做
海绵宝宝转agent1 小时前
LeetCodehot100题解
java·笔记·学习·算法·leetcode·深度优先
SunnyDays10111 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java·word
SunnyDays10111 小时前
Java 为 Word 添加页码:隐藏封面页码、从指定页开始编号、奇偶页页码设置等
java·word·页码
开源推荐官2 小时前
多门店 O2O 商城选型复盘:从开发视角对比主流开源系统
java·架构·开源·php
吴名氏.2 小时前
Java转AI Agent教程(第二周)超时重试与错误分类
java·人工智能·python·分类·ai agent
从琳开始9892 小时前
从Socket到UDP回显服务器:Java网络编程完整指南
java·网络·网络协议·udp·java-ee
西瓜拿铁好喝3 小时前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库
学长毕设3 小时前
基于SpringBoot的校园礼服租赁系统的设计与实现
java·spring boot·后端