力扣Hot100题,刷题

力扣HOT100 - 1. 两数之和
在这里插入图片描述
解题思路:

解法一:暴力

class Solution {public int[] twoSum(int[] nums, int target) {int n = nums.length;for (int i = 0; i < n; i++)for (int j = i + 1; j < n; j++) {if (target == nums[i] + nums[j])return new int[] { i, j };}return new int[0];}
}

解法二:哈希表

class Solution {public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; ++i) {if (map.containsKey(target - nums[i])) {//保证返回的下标由小到大return new int[]{map.get(target - nums[i]), i};}map.put(nums[i], i);}return new int[0];}
}

解析:
这段代码实现的是经典的 两数之和(Two Sum) 算法,通过使用哈希表(HashMap)来优化时间复杂度。以下是对代码的详细解析:


代码功能

  • 输入:一个整数数组 nums 和一个目标值 target
  • 输出:返回数组中两个不同下标 ij,使得 nums[i] + nums[j] == target
    • 如果存在解,保证返回的下标 i < j(按升序排列)。
    • 如果无解,返回空数组 new int[0]

算法思路

  1. 哈希表辅助

    • 使用 HashMap 存储数组元素值 (nums[i]) 和对应的下标 (i)。
    • 对于当前元素 nums[i],检查 target - nums[i] 是否已存在于哈希表中:
      • 如果存在,说明找到了这两个数,直接返回它们的下标。
      • 如果不存在,将当前 nums[i]i 存入哈希表,继续遍历。
  2. 时间复杂度

    • O(n):只需遍历数组一次,哈希表的插入和查询操作平均为 O(1)。
    • 比暴力解法(O(n²))更高效。
  3. 空间复杂度

    • O(n):最坏情况下需要存储所有元素到哈希表。

代码逐行解析

public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();  // 键:数组元素值,值:下标for (int i = 0; i < nums.length; ++i) {if (map.containsKey(target - nums[i])) {  // 检查是否已有互补数return new int[]{map.get(target - nums[i]), i};  // 返回升序下标}map.put(nums[i], i);  // 存入当前数和下标}return new int[0];  // 无解时返回空数组
}

关键点

  1. 为什么能保证下标升序?

    • 当发现 target - nums[i] 在哈希表中时,该数一定是在 nums[i] 之前被存入的(因为当前 nums[i] 还未存入)。
    • 因此 map.get(target - nums[i]) 获取的下标一定小于 i,直接返回 {map.get(...), i} 即满足升序。
  2. 如何处理重复元素?

    • 例如 nums = [3, 3], target = 6
      • 第一个 3 存入哈希表(map = {3: 0})。
      • 第二个 3 时,target - 3 = 3 已在哈希表中,直接返回 [0, 1]
    • 注意:如果问题要求不能重复使用同一元素(如 nums = [3], target = 6),需额外判断(但本题约束保证解存在或唯一)。
  3. 为什么返回 new int[0] 而不是 null

    • 空数组更安全,调用方无需判空,可直接通过 result.length == 0 判断无解。

示例运行

  • 输入nums = [2, 7, 11, 15], target = 9

    • 步骤:
      1. i=02 的互补数 7 不在哈希表 → 存入 {2: 0}
      2. i=17 的互补数 2 在哈希表 → 返回 [0, 1]
  • 输入nums = [3, 2, 4], target = 6

    • 返回 [1, 2](因为 2 + 4 = 6,且下标升序)。

边界情况

  1. 无解时:如 nums = [1, 2], target = 4 → 返回 []
  2. 重复元素:如 nums = [3, 3], target = 6 → 返回 [0, 1]
  3. 负数或零:如 nums = [-1, 0], target = -1 → 返回 [0, 1]

优化与变种

  1. 提前返回:一旦找到解立即返回,减少不必要的遍历。
  2. 处理重复元素:如果问题要求所有可能的解(不止一个),可以用 List 存储结果。
  3. 排序+双指针:如果需返回数值而非下标,可先排序后用双指针(但会改变下标信息)。

总结

  • 该实现是两数之和问题的最优解之一,兼顾了时间复杂度和代码简洁性。
  • 核心思想:用空间换时间,通过哈希表避免嵌套循环。

如果有其他变种问题(如三数之和、允许重复使用元素等),可以进一步讨论!

力扣HOT100 - 49. 字母异位词分组
在这里插入图片描述
注意:
返回时不能用List,因为List是抽象类,return的必须是List的具体实现,如ArrayList

class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> map = new HashMap<>();for (String str : strs) {char[] array = str.toCharArray();Arrays.sort(array);String key = new String(array);List<String> list = map.getOrDefault(key, new ArrayList<String>());list.add(str);map.put(key, list);}//返回时不能用List,而必须是List的具体实现,如ArrayListreturn new ArrayList<List<String>>(map.values());}
}

解析:
这段代码实现的是 字母异位词分组(Group Anagrams) 问题,通过将每个字符串排序后的结果作为键,将字母异位词归类到同一个列表中。以下是详细解析:


问题描述

  • 输入:一个字符串数组 strs(例如 ["eat","tea","tan","ate","nat","bat"])。
  • 输出:将字母异位词(由相同字母重排列组成的字符串)分到同一组,返回分组后的列表(如 [["eat","tea","ate"], ["tan","nat"], ["bat"]])。

算法思路

  1. 哈希表分组

    • 使用 HashMap,键是 排序后的字符串(字母异位词排序后相同),值是对应的原始字符串列表。
    • 遍历每个字符串,先排序其字符数组,生成统一的键,再将原始字符串加入对应的列表。
  2. 关键操作

    • 排序字符数组:将字符串转换为 char[] 并排序,使得字母异位词具有相同的键(如 "eat""tea" 均排序为 "aet")。
    • 哈希表管理:使用 map.getOrDefault() 避免重复创建列表。
  3. 复杂度分析

    • 时间复杂度:O(n * k log k),其中 n 是数组长度,k 是字符串最大长度(排序每个字符串的代价)。
    • 空间复杂度:O(n * k),哈希表存储所有字符串。

代码逐行解析

public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> map = new HashMap<>();  // 键:排序后的字符串,值:原始字符串列表for (String str : strs) {char[] array = str.toCharArray();  // 转为字符数组Arrays.sort(array);                // 排序(字母异位词排序后相同)String key = new String(array);    // 生成统一键List<String> list = map.getOrDefault(key, new ArrayList<String>()); // 获取或新建列表list.add(str);                     // 加入当前字符串map.put(key, list);                // 更新哈希表}return new ArrayList<List<String>>(map.values()); // 返回所有分组
}

关键点

  1. 为什么排序字符数组?

    • 字母异位词排序后结果相同(如 "eat""aet""tea""aet"),可作为分组的唯一标识。
  2. map.getOrDefault() 的作用

    • 若键不存在,返回默认值(新建的 ArrayList);若存在,直接返回对应的列表。避免手动检查键是否存在。
  3. 返回值处理

    • map.values() 返回所有分组的集合,通过 new ArrayList<>(...) 包装成 List<List<String>> 类型。

示例运行

  • 输入strs = ["eat","tea","tan","ate","nat","bat"]
    • 步骤:
      1. "eat" → 排序为 "aet" → 新建列表 ["eat"]
      2. "tea" → 排序为 "aet" → 加入列表 ["eat", "tea"]
      3. "tan" → 排序为 "ant" → 新建列表 ["tan"]
      4. 最终分组:{"aet": ["eat","tea","ate"], "ant": ["tan","nat"], "abt": ["bat"]}
    • 输出[["eat","tea","ate"], ["tan","nat"], ["bat"]]

优化与变种

  1. 优化键的生成

    • 若字符串只含小写字母,可用 字符计数数组 作为键(如 "aab" 表示为 [2,1,0,...,0]),避免排序开销:
      int[] count = new int[26];
      for (char c : str.toCharArray()) count[c - 'a']++;
      String key = Arrays.toString(count);  // 将计数数组转为字符串作为键
      
      • 时间复杂度降至 O(n * k)。
  2. 处理大写/特殊字符

    • 扩展字符计数数组的长度(如 ASCII 码范围)。
  3. 返回其他结构

    • 若需按组内字符串数量排序,可对 map.values() 进行排序。

边界情况

  1. 空数组输入strs = [] → 返回 []
  2. 所有字符串相同strs = ["a","a"] → 返回 [["a","a"]]
  3. 无字母异位词strs = ["a","b"] → 返回 [["a"], ["b"]]

总结

  • 该解法通过 排序统一键字符计数 高效分组,是字母异位词问题的标准解法。
  • 选择排序还是计数取决于字符集大小(小写字母优先用计数)。

如果有其他需求(如按字典序输出分组),可以进一步调整!

力扣HOT100 - 128. 最长连续序列
在这里插入图片描述
解题思路:

注意:

1.Set不能直接排序,必须要转换成ArrayList或者LinkedList后用Collections.sort()方法进行排序。

(Queue也不能直接排序,排序方法同Set)

2.连续的序列不能只找第一个,因为不一定是最长的,需要全部遍历完才能找到最长序列。

class Solution {public int longestConsecutive(int[] nums) {if (nums.length == 0)  return 0;Set<Integer> set = new HashSet<>();for (int i = 0; i < nums.length; i++) {set.add(nums[i]);}List<Integer> list = new ArrayList<>(set);Collections.sort(list);//cnt的长度从1开始,本身长度为1int cnt = 1;int max = 0;//减一是为了避免越界for (int i = 0; i < list.size() - 1; i++) {if (list.get(i) == list.get(i + 1) - 1) {cnt++;} else {max = Math.max(cnt, max);//重置cnt的值cnt = 1;}}max = Math.max(cnt, max);return max;}
}```
解析:
这段代码实现了 **最长连续序列(Longest Consecutive Sequence** 问题,但存在一些可以优化的地方。以下是详细解析和改进建议:---### **原代码分析**
#### **思路**
1. **去重**:使用 `HashSet` 去除重复数字。
2. **排序**:将去重后的数字存入 `ArrayList` 并排序。
3. **遍历统计**:检查相邻数字是否连续,统计最长连续序列长度。#### **复杂度**
- **时间复杂度**O(n log n),主要由排序步骤决定(`Collections.sort()`)。
- **空间复杂度**O(n),用于存储去重后的数字。#### **问题**
- **排序不必要**:题目要求连续序列(如 `[1,2,3]`),但排序增加了时间复杂度。
- **边界处理冗余**:末尾需要额外 `Math.max(cnt, max)` 处理最后一个连续序列。---### **优化思路**
利用 **哈希集合** 直接寻找连续序列的起点,避免排序:
1. **将所有数字存入集合**(去重)。
2. **遍历集合**,仅当当前数字 `num` 是某个连续序列的起点(即 `num-1` 不在集合中)时,向后检查 `num+1, num+2,...` 是否存在,统计长度。
3. **更新最大值**。#### **优化后复杂度**
- **时间复杂度**O(n),每个数字最多被访问两次(一次在集合中,一次在连续序列检查中)。
- **空间复杂度**O(n)---### **优化后的代码**
```java
class Solution {public int longestConsecutive(int[] nums) {if (nums.length == 0) return 0;Set<Integer> set = new HashSet<>();for (int num : nums) {set.add(num); // 去重}int maxLen = 1;for (int num : set) {// 只有当 num 是连续序列的起点时才检查(避免重复计算)if (!set.contains(num - 1)) {int currentNum = num;int currentLen = 1;// 向后查找连续的数字while (set.contains(currentNum + 1)) {currentNum++;currentLen++;}maxLen = Math.max(maxLen, currentLen);}}return maxLen;}
}

关键改进点

  1. 避免排序:直接利用集合的 O(1) 查询特性。
  2. 高效检查连续序列:仅对可能的序列起点(num-1 不在集合中)进行检查。
  3. 边界处理简化:无需处理排序后的末尾特殊情况。

示例运行

  • 输入nums = [100, 4, 200, 1, 3, 2]
    • 步骤
      1. 集合:{1, 2, 3, 4, 100, 200}
      2. 检查起点:
        • 10 不在集合中)→ 连续序列 1,2,3,4,长度 4。
        • 10099 不在集合中)→ 连续序列 100,长度 1。
        • 200199 不在集合中)→ 连续序列 200,长度 1。
    • 输出4

边界情况

  1. 空数组:直接返回 0
  2. 所有数字相同:如 [1,1,1] → 去重后 [1],返回 1
  3. 无连续序列:如 [5, 7, 9] → 每个数字单独成组,返回 1

总结

  • 原始代码:通过排序解决问题,但时间复杂度过高(O(n log n))。
  • 优化后代码:利用哈希集合将时间复杂度降至 O(n),更高效。
  • 适用场景:当输入规模较大时(如 n > 10^5),优化后的方法优势明显。

力扣HOT100 - 283. 移动零
在这里插入图片描述
解题思路:

双指针

指针 i 用于寻找不为零的位置

指针 j 用于寻找为零的位置

找到不为零时位置时与为零的位置进行交换,即nums[ i ]与nums[ j ]交换,使零向后移动,i 和 j 同时向下一个位置移动。

class Solution {public void moveZeroes(int[] nums) {if (nums == null) return;            int j = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] != 0) {int tmp = nums[i];nums[i] = nums[j];nums[j++] = tmp;//j++所到的位置还是零}}}
}

力扣HOT100 - 11. 盛最多水的容器
在这里插入图片描述
解析

class Solution {public int maxArea(int[] height) {int i = 0, j = height.length - 1, res = 0;while(i < j) {res = height[i] < height[j] ? Math.max(res, (j - i) * height[i++]): Math.max(res, (j - i) * height[j--]); }return res;}
}

易错点:Math.max(res , (j - i) * height[i++])和Math.max(res , height[i++] * (j - i) )结果不同

这两个表达式的结果不同,是因为 i++ 的自增操作时机影响了 (j - i) 的计算结果。具体差异如下:


表达式 1:Math.max(res, (j - i) * height[i++])

  1. 执行顺序

    • 先计算 (j - i)(此时 i 未自增)。
    • 再计算 height[i](此时 i 仍未自增)。
    • 最后进行 i++(自增发生在整个表达式计算完成后)。
  2. 等效代码

    int old_i = i;                // 保存当前i的值
    int width = j - old_i;        // 宽度计算使用旧的i
    int h = height[old_i];        // 高度使用旧的i
    i = old_i + 1;                // i自增
    res = Math.max(res, width * h);
    

表达式 2:Math.max(res, height[i++] * (j - i))

  1. 执行顺序

    • 先计算 height[i](此时 i 未自增)。
    • 然后执行 i++(自增立即发生,影响后续的 (j - i))。
    • 最后计算 (j - i)(此时 i 已经自增,宽度比预期少1)。
  2. 等效代码

    int old_i = i;                // 保存当前i的值
    int h = height[old_i];        // 高度使用旧的i
    i = old_i + 1;                // i自增(立即生效)
    int width = j - i;            // 宽度计算使用新的i(比预期少1)
    res = Math.max(res, h * width);
    

关键区别

表达式(j - i) 的计算时机height[i] 的索引实际效果
(j - i) * height[i++]i++ 之前i宽度正确,高度正确
height[i++] * (j - i)i++ 之后i高度正确,宽度少1

示例说明

假设 i = 2, j = 5, height = [1, 3, 2, 4]

  1. 表达式 1

    • (j - i)5 - 2 = 3
    • height[i]height[2] = 2
    • 结果:3 * 2 = 6(正确)
    • 执行后 i = 3
  2. 表达式 2

    • height[i]height[2] = 2
    • i++i 变为 3
    • (j - i)5 - 3 = 2
    • 结果:2 * 2 = 4(宽度少1,错误)
    • 执行后 i = 3

如何避免问题?

  1. 分离自增操作
    int h = height[i];
    i++;
    res = Math.max(res, (j - i + 1) * h); // 注意宽度补偿
    
  2. 使用 i+1 显式计算
    res = Math.max(res, (j - (i + 1)) * height[i]);
    i++;
    

总结

  • 运算符优先级和求值顺序i++ 的副作用会影响同一表达式中其他部分的计算。
  • 防御性编程:在复杂表达式中,避免混用自增操作和依赖变量值的计算。

这种差异常见于双指针或滑动窗口算法中(如盛水容器问题)。务必确保宽度和高度的计算基于正确的索引!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/75995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app ucharts自定义换行tooltips

实现效果&#xff1a; 第一步&#xff1a;在uni_modules文件夹下找到config-ucharts.js和u-charts.js文件 第二步&#xff1a;在config-ucharts.js文件中配置换行格式 // 换行格式"wrapTooltip":function(item, category, index, opts){return item.name&#xff1a;…

国标GB28181视频平台EasyCVR顺应智慧农业自动化趋势,打造大棚实时视频监控防线

一、方案背景 近年来&#xff0c;温室大棚种植技术凭借其显著的优势&#xff0c;在提升农作物产量和质量、丰富农产品供应方面发挥了重要的作用&#xff0c;极大改善了人们的生活水平&#xff0c;得到了广泛的推广和应用。大棚内的温度、湿度、光照度和二氧化碳浓度等环境因素…

InternVideo2.5:Empowering Video MLLMs with Long and Rich Context Modeling

一、TL&#xff1b;DR InternVideo2.5通过LRC建模来提升MLLM的性能。层次化token压缩和任务偏好优化&#xff08;mask时空 head&#xff09;整合到一个框架中&#xff0c;并通过自适应层次化token压缩来开发紧凑的时空表征MVBench/Perception Test/EgoSchema/MLVU数据benchmar…

【时时三省】(C语言基础)条件运算符和条件表达式

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 有一种if语句&#xff0c;当被判别的表达式的值为“真”或“假”时&#xff0c;都执行一个赋值语句且向一个变量赋值。 如&#xff1a; if ( a > b ) max a&#xff1b; else max …

KWDB创作者计划—边缘计算:从概念到落地的技术解读

引言 随着物联网&#xff08;IoT&#xff09;和人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;数据量呈爆炸式增长&#xff0c;传统的云计算架构逐渐暴露出延迟高、带宽占用大等问题。边缘计算作为一种新兴的分布式计算范式&#xff0c;正在改变数据处理的方式。本…

蓝桥杯基础算法-递归

代码简洁&#xff0c;但涉及到的运算&#xff0c;会随着递归层数的增加成指数级增长 路分析&#xff1a;第20行20列位于45度这条线上 这条线上的数字是1 5 13 25 41...两数之差:4 8 12 16 --->每一个都是在前面的基础上4&#xff0c;可以用递归或者循环 public class dem…

通过学习opencv图像库编程借助第三方库函数完成一个综合程序设计

通过学习opencv图像库编程借助第三方库函数完成一个综合程序设计 1) 编译命令解释&#xff1a; 编译命令&#xff1a; gcc test1.cpp -o test1 pkg-config --cflags --libs opencv这条命令包含了以下部分&#xff1a; gcc test1.cpp -o test1: gcc 是 GNU 编译器集合&#…

第十四届蓝桥杯大赛软件赛国赛C/C++研究生组

研究生C国赛软件大赛 题一&#xff1a;混乘数字题二&#xff1a;钉板上的正方形题三&#xff1a;整数变换题四&#xff1a;躲炮弹题五&#xff1a;最大区间 题一&#xff1a;混乘数字 有一点像哈希表&#xff1a; 首先定义两个数组&#xff0c;拆分ab和n 然后令n a*b 查看两个…

系统与网络安全------网络通信原理(2)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 物理层解析 物理层概述 物理层是TCP/IP模型的最底层物理层数据传输提供稳定的物理连接 物理层功能 定义设备的物理连接的标准和特性&#xff0c;比如接口形状&#xff0c;大小定义电气特性&#xff0c;高低…

内容中台的数字化管理核心是什么?

数字化整合与系统协同 现代企业的内容管理正经历从分散式架构向数字化整合的范式转变。通过将内容管理系统与文档管理技术深度耦合&#xff0c;组织能够打破数据孤岛&#xff0c;实现跨部门、跨平台的资源互通。例如&#xff0c;基于元数据分类的标准化体系&#xff0c;不仅提…

Python爬虫第二战(使用xpath爬取网站数据)

本文是我在学习过程中记录学习的点点滴滴&#xff0c;目的是为了学完之后巩固一下顺便也和大家分享一下&#xff0c;日后忘记了也可以方便快速的复习。 使用xpath爬取猪八戒网站数据 前言 前言 今天学习的主要是关于Python使用xpath来爬取猪八戒网的网页知识的理解和应用 #1.获…

进程同步和进程互斥的区别

如大家所了解的&#xff0c;进程互斥是由互斥资源引起的&#xff0c;即各进程之间共享互斥资源的使用权&#xff0c;这种竞争没有确定的必然联系&#xff0c;哪个进程竞争到互斥资源的使用权&#xff0c;则该资源就归哪个进程使用&#xff0c;从而获得所需资源的进程就可以获得…

ArkTS语言基础之函数

前言 臭宝们终于来到了ArkTS基础之函数&#xff0c;今天我们来学习一下ArkTS的函数的相关知识&#xff0c;上一节中也有一些函数的基础知识。 函数声明 函数声明引入一个函数&#xff0c;包含其名称、参数列表、返回类型和函数体,在下面的例子中&#xff0c;我们声明了一个名…

redis中的hash

Redis中的hash是什么 Hash: 哈希&#xff0c;也叫散列&#xff0c;是一种通过哈希函数将键映射到表中位置的数据结构&#xff0c;哈希函数是关键&#xff0c;它把键转换成索引。 Redis Hash&#xff08;散列表&#xff09;是一种 field-value pairs&#xff08;键值对&#x…

弹簧质点系统(C++实现)

本文实现一个简单的物理算法&#xff1a;弹簧质点系统&#xff08;Mass-Spring System&#xff09;。这是一个经典的物理模拟算法&#xff0c;常用于模拟弹性物体&#xff08;如布料、弹簧等&#xff09;的行为。我们将使用C来实现这个算法&#xff0c;并结合链表数据结构来管理…

领域大模型

领域技术标准文档或领域相关数据是领域模型Continue PreTrain的关键。 现有大模型在预训练过程中都会加入书籍、论文等数据&#xff0c;那么在领域预训练时这两种数据其实也是必不可少的&#xff0c;主要是因为这些数据的数据质量较高、领域强相关、知识覆盖率&#xff08;密度…

Wincc项目被锁定无法打开

Wincc项目被锁定无法打开 解决方法 解决方法 一般这种情况是因为项目打开的时候直接关机导致的。 删除项目文件夹的ProjectOpened.lck的文件夹即可 然后即可正常打开项目

SpringBoot3笔记

简介&#xff1a; springboot整合了springframework&#xff0c;整合了许多配置&#xff0c;让我们能够快速创建一个以springframework为基础的项目。 问题&#xff1a; 到目前为止&#xff0c;你已经学习了多种配置Spring程序的方式。但是无论使用XML、注解、Java配置类还是…

DeepSeek和文心一言的区别

文章目录 1.开发公司&#xff1a;2.应用场景&#xff1a;3.训练数据&#xff1a;4.模型架构&#xff1a;5.技术特点&#xff1a;6.语言风格&#xff1a;7.开源性&#xff1a;8.界面与用户体验&#xff1a; 1.开发公司&#xff1a; DeepSeek 由杭州深度求索人工智能基础技术研究…

Windows 10 安装Mysql 8

安装准备 下载 MySQL Windows ZIP版&#xff08;无安装程序&#xff09;&#xff1a; 进入官网&#xff1a;https://dev.mysql.com/downloads/mysql/ 选择 Windows → 下载 ZIP Archive&#xff0c;例如&#xff1a; mysql-8.0.34-winx64.zip 如果你的电脑没有安装 VC Redi…