当前位置: 首页 > news >正文

力扣top100(day01-03)

560. 和为 K 的子数组

class Solution {public int subarraySum(int[] nums, int k) {int count = 0;for (int i = 0; i < nums.length; i++) {int sum = 0;for (int j = i; j < nums.length; j++) {sum += nums[j];if (sum == k) {count++;}// else if(sum>k){//     continue;// }}}return count;}
}
public class Solution {public int subarraySum(int[] nums, int k) {// count:记录满足条件的子数组数量// pre:当前的前缀和(nums[0] + nums[1] + ... + nums[i])int count = 0, pre = 0;// mp:哈希表,key 是前缀和,value 是该前缀和出现的次数HashMap<Integer, Integer> mp = new HashMap<>();// 初始化:前缀和为 0 的情况出现 1 次(空子数组的和为 0)mp.put(0, 1);// 遍历数组,计算前缀和for (int i = 0; i < nums.length; i++) {// 更新当前的前缀和pre += nums[i];// 检查是否存在 pre - k 的前缀和// 如果存在,说明从某个位置到当前位置的子数组和为 kif (mp.containsKey(pre - k)) {// 累加 pre - k 的出现次数到 countcount += mp.get(pre - k);}// 将当前前缀和存入哈希表,并更新出现次数mp.put(pre, mp.getOrDefault(pre, 0) + 1);}// 返回满足条件的子数组数量return count;}
}

关键逻辑注释

1. mp.put(0, 1) 的作用
  • 为什么需要初始化 mp.put(0, 1)
    因为空子数组(不选任何元素)的和为 0。如果某个前缀和直接等于 k(比如 nums[0..i] == k),我们需要 pre - k = 0 的情况能被统计到。

2. if (mp.containsKey(pre - k)) 的逻辑
  • 为什么 pre - k 能表示子数组和为 k
    假设 pre[j] 是当前前缀和,pre[i] 是之前某个前缀和,如果 pre[j] - pre[i] = k,那么子数组 nums[i+1..j] 的和就是 k

    • 举例
      nums = [1, 2, 3]k = 3

      • i = 1 时,pre = 1 + 2 = 3pre - k = 0mp 中有 0),说明子数组 [1, 2] 的和为 3

      • i = 2 时,pre = 1 + 2 + 3 = 6pre - k = 3mp 中有 3),说明子数组 [3] 的和为 3

3. mp.put(pre, ...) 的作用
  • 为什么需要更新哈希表?
    记录每个前缀和的出现次数,后续可以通过 pre - k 快速判断是否存在满足条件的子数组。


示例运行(nums = [1, 1, 1]k = 2

步骤inums[i]prepre - kmp 更新前count 变化mp 更新后
初始化--0-{0: 1}--
1011-1{0: 1}{0: 1, 1: 1}
21120{0: 1, 1: 1}count += 1{0: 1, 1: 1, 2: 1}
32131{0: 1, 1: 1, 2: 1}count += 1{0: 1, 1: 1, 2: 1, 3: 1}
  • 结果count = 2(子数组 [1, 1] 和 [1, 1])。


总结

  • 核心思想:用前缀和和哈希表将问题转化为两数之差问题。

  • 时间复杂度:O(n),只需遍历一次数组。

  • 空间复杂度:O(n),哈希表存储前缀和。

53. 最大子数组和

class Solution {public int maxSubArray(int[] nums) {// pre:以当前元素结尾的子数组的最大和// maxAns:全局最大子数组和int pre = 0, maxAns = nums[0];for (int x : nums) {// 决定是否抛弃之前的子数组,或加入当前元素pre = Math.max(pre + x, x);// 更新全局最大值maxAns = Math.max(maxAns, pre);}return maxAns;}
}

这段代码使用 动态规划(Kadane算法) 高效求解 最大子数组和 问题。它的核心思想是:

  1. 维护两个变量

    • pre:表示以当前元素结尾的子数组的最大和。

    • maxAns:记录全局最大子数组和。

  2. 递推关系

    • 对于每个元素 x,决定是将其加入前面的子数组(pre + x),还是以它作为新子数组的起点(x)。

    • 更新 maxAns 为当前最大值。


逐行解析

1. 初始化

java

int pre = 0, maxAns = nums[0];
  • pre:初始化为 0,表示尚未开始累加。

  • maxAns:初始化为 nums[0],因为至少包含一个元素的子数组的最大和可能是第一个元素本身。


2. 遍历数组

java

for (int x : nums) {pre = Math.max(pre + x, x);maxAns = Math.max(maxAns, pre);
}
  • pre = Math.max(pre + x, x)

    • pre + x:将当前元素 x 加入之前的子数组。

    • x:以当前元素 x 作为新子数组的起点。

    • 选择较大值,确保 pre 始终是以 x 结尾的子数组的最大和。

  • maxAns = Math.max(maxAns, pre)

    • 更新全局最大值 maxAns


示例演示

假设 nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]

元素 (x)pre (更新前)pre + xx新 premaxAns (更新前)新 maxAns
-20-2-2-2-2-2
1-2-111-21
-31-2-3-211
4-224414
-143-1344
2352545
1561656
-561-5166
4154566
  • 最终结果maxAns = 6(对应子数组 [4, -1, 2, 1])。


复杂度分析

  • 时间复杂度:O(n),只需遍历一次数组。

  • 空间复杂度:O(1),仅使用常数空间。


关键点总结

  1. 动态规划状态转移

    • pre 表示以当前元素结尾的子数组的最大和。

    • 通过比较 pre + x 和 x 决定是否抛弃之前的子数组。

  2. 全局最大值更新

    • 每次迭代后更新 maxAns,确保不遗漏任何可能的子数组。

  3. 初始化技巧

    • maxAns 初始化为 nums[0],处理数组全为负数的情况

56. 合并区间

class Solution {public int[][] merge(int[][] intervals) {if (intervals.length <= 1) {return intervals;}// 按区间起始点排序Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));List<int[]> merged = new ArrayList<>();int[] current = intervals[0];merged.add(current);for (int[] interval : intervals) {if (interval[0] <= current[1]) {// 合并区间current[1] = Math.max(current[1], interval[1]);} else {// 无法合并,添加到结果current = interval;merged.add(current);}}return merged.toArray(new int[merged.size()][]);}
}
1. 输入处理与特殊情况

java

if (intervals.length <= 1) {return intervals;
}
  • 作用:如果输入的区间数量 ≤ 1(即空数组或单个区间),直接返回原数组。

  • 原因:无需合并,直接返回即可。


2. 区间排序

java

Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
  • 作用:按区间的起始点(intervals[i][0])升序排序。

  • 原因:排序后,重叠的区间会相邻排列,便于后续合并。

  • 示例

    • 输入:[[2,6],[1,3],[8,10],[15,18]]

    • 排序后:[[1,3],[2,6],[8,10],[15,18]]


3. 初始化合并列表

java

List<int[]> merged = new ArrayList<>();
int[] current = intervals[0];
merged.add(current);
  • merged:用于存储合并后的结果区间。

  • current:当前正在处理的区间,初始化为第一个区间。

  • 原因:从第一个区间开始逐步合并。


4. 遍历与合并区间

java

for (int[] interval : intervals) {if (interval[0] <= current[1]) {// 合并区间current[1] = Math.max(current[1], interval[1]);} else {// 无法合并,添加到结果current = interval;merged.add(current);}
}
  • 逻辑

    1. 可合并:如果当前区间的起始点 ≤ current 的结束点,说明两区间重叠。

      • 更新 current 的结束点为两者的最大值(扩展区间)。

      • 示例

        • current = [1,3]interval = [2,6] → 合并为 [1,6]

    2. 不可合并:如果区间不重叠,将 current 加入结果,并更新 current 为新区间。

      • 示例

        • current = [1,6]interval = [8,10] → 无法合并,将 [1,6] 加入结果,current 更新为 [8,10]


5. 返回结果

java

return merged.toArray(new int[merged.size()][]);
  • 作用:将 List<int[]> 转换为 int[][]

  • 原因:题目要求返回二维数组。

深入解析 Comparator.comparingInt(a -> a[0])

这是Java中用于排序的一个关键表达式,属于函数式编程的用法。我们来彻底理解它的含义和工作原理。


1. 基本作用

java

Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
  • 功能:对二维数组 intervals 按照每个子数组的第一个元素(即区间起点)进行升序排序。

  • 等价于

    java

    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

2. 分解解析
(1) Comparator 接口
  • 是Java中用于定义比较规则的接口。

  • 核心方法:int compare(T o1, T o2)

(2) comparingInt() 方法
  • 是 Comparator 的静态工厂方法。

  • 签名:

    java

    static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
  • 参数ToIntFunction,即从对象中提取一个 int 类型排序键的函数。

  • 作用:根据提取的 int 键生成一个 Comparator

(3) a -> a[0]
  • 是一个Lambda表达式,等价于:

    java

    new ToIntFunction<int[]>() {@Overridepublic int applyAsInt(int[] a) {return a[0];}
    }
  • 功能:输入一个区间 a,返回它的起始点 a[0]


3. 完整执行流程
  1. 提取排序键:对每个区间 a,提取 a[0](起始点)。

  2. 比较规则:根据提取的 a[0] 值生成默认的升序比较器。

    • 相当于隐式实现了:

      java

      (a, b) -> Integer.compare(a[0], b[0])

4. 对比其他写法
写法说明
Comparator.comparingInt(a -> a[0])函数式风格,简洁
(a, b) -> a[0] - b[0]传统Lambda,可能溢出
new Comparator<int[]>() { ... }匿名类,冗长

5. 为什么用 comparingInt
  1. 避免整数溢出

    • a[0] - b[0] 在极大值时可能溢出。

    • Integer.compare(a[0], b[0]) 会正确处理边界。

  2. 代码可读性

    • 明确表达"按某字段排序"的意图。

  3. 类型安全

    • 编译期检查提取的键是否为 int


6. 扩展用法
(1) 降序排序

java

Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]).reversed());
(2) 多级排序

java

// 先按起点升序,再按终点降序
Arrays.sort(intervals,Comparator.comparingInt((int[] a) -> a[0]).thenComparing(Comparator.comparingInt(a -> a[1]).reversed())
);

7. 实际应用示例

输入[[3,4],[1,2],[2,3]]
排序后[[1,2],[2,3],[3,4]]
代码执行

  1. 提取每个元素的 a[0]3, 1, 2

  2. 按提取值排序:1, 2, 3

  3. 最终顺序:[1,2][2,3][3,4]

http://www.dtcms.com/a/328297.html

相关文章:

  • 基于RAII的智能指针原理和模拟实现智能指针
  • MySQL与其他数据库产品的比较,优势在哪里?
  • 《坐庄》电视剧
  • 基于Python的海量电商用户行为分析与可视化【推荐算法、统计模型、聚类模型、电商指标维度分析】
  • 【4】Transformers快速入门:自然语言模型 vs 统计语言模型
  • [激光原理与应用-257]:理论 - 几何光学 - 光束整形
  • 锁性能基准测试
  • 石英加速度计如何实现高精度测量?
  • 明远智睿T113-i核心板:工业设备制造领域的革新利器
  • 具身智能竞速时刻,百度百舸提供全栈加速方案
  • JVM性能调优技巧
  • Java集合学习之forEach()遍历方法的底层原理
  • 数据科学与计算:爬虫和数据分析案例笔记
  • 01数据结构-Kruskal算法
  • 破译真实感:渲染参数进阶指南——告别塑料感,唤醒材质生命力
  • 01. maven的下载与配置
  • ubuntu24下keychorn键盘连接不了的改建页面的问题修复
  • “生成式UI革命”:Tambo AI如何让你的应用“开口说话、动手搭界面” | 全面深剖、案例实践与未来展望
  • Seed-VC:零样本语音转换与扩散transformer
  • 08--深入解析C++ list:高效操作与实现原理
  • 从爬虫新手到DrissionPage实践者的技术旅程
  • 【IP查询】使用IP66(ip66.net)验证IP地址定位的准确率
  • 小智智能交互算法通过国家备案,视觉大模型引领AI应用新浪潮
  • 机器学习之TF-IDF文本关键词提取
  • 终端安全检测与防御技术
  • 数据结构:中缀到后缀的转换(Infix to Postfix Conversion)
  • 【速通版!语义通信基础与前沿学习计划】
  • C++中类之间的关系详解
  • AR巡检:三大核心技术保障数据准确性
  • Langchain入门:构建一个PDF摄取和问答系统