【码道初阶】Leetcode771 宝石与石头:Set 判成员 vs List 判成员(同题两种写法的差距)

771 宝石与石头:Set 判成员 vs List 判成员(同题两种写法的差距)

题目给两个字符串:

  • jewels:宝石的种类(每个字符都是一种宝石类型,而且保证不重复)
  • stones:我拥有的石头(每个字符是一块石头的类型)

目标:统计 stones 中有多少字符出现在 jewels 里。

题目还强调一点:区分大小写 ,所以 'a''A' 不是一种类型。


1. 把题目翻译成"算法语言"

这是一个标准的"集合成员判断 + 计数"问题:

  • 先把 jewels 的所有字符放到一个"可快速判断是否存在"的结构里
  • 再遍历 stones,每看到一个字符就问一句:"它是不是宝石?"
  • 如果是,计数 +1

关键就在"可快速判断是否存在"的数据结构选型。


2. 解法一:HashSet(最贴合题意,平均 O(1) 成员判断)

java 复制代码
class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        HashSet<Character> set = new HashSet<>();
        for(int i = 0;i<jewels.length();i++){
           set.add(jewels.charAt(i));
        }
        int hasJews = 0;
        for(int i = 0;i<stones.length();i++){
            if(set.contains(stones.charAt(i))) hasJews ++;
        }
        return hasJews;
    }
}

核心思路

HashSet 的天然用途就是:去重 + 快速判断是否存在

  • set.add(x):把元素放进集合
  • set.contains(x):判断元素是否在集合里(平均 O(1))

题目保证 jewels 的字符唯一,这跟 set 的特性完美契合。

代码逻辑拆解

java 复制代码
HashSet<Character> set = new HashSet<>();
for (int i = 0; i < jewels.length(); i++) {
    set.add(jewels.charAt(i));
}

这一步是"建宝石集合":把每种宝石类型存到 set 中。

接着遍历 stones 并计数:

java 复制代码
int hasJews = 0;
for (int i = 0; i < stones.length(); i++) {
    if (set.contains(stones.charAt(i))) hasJews++;
}
return hasJews;

每块石头只做一次 contains 判断,是宝石就加一。

复杂度

  • 建 set:O(|jewels|)
  • 遍历 stones:O(|stones|),每次 contains 平均 O(1)
  • 总体:O(|jewels| + |stones|)
  • 空间:O(|jewels|)

这种写法属于"题目想让我写的答案"。


3. 解法二:List + contains(能做对,但成员判断慢很多)

java 复制代码
class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        List<Character> jew = new ArrayList<>();
        int hasJews = 0;
        for(int i = 0;i<jewels.length();i++){
            jew.add(jewels.charAt(i));
        }
        for(int i = 0;i<stones.length();i++){
            if(jew.contains(stones.charAt(i))) hasJews++;
        }
        return hasJews;
    }
}

核心思路

先把 jewels 存到 List<Character> 里,然后对 stones 的每个字符调用 list.contains() 来判断是否是宝石。

代码逻辑拆解

java 复制代码
List<Character> jew = new ArrayList<>();
for(int i = 0; i < jewels.length(); i++){
    jew.add(jewels.charAt(i));
}

这一步把宝石类型顺序放进列表。

计数阶段:

java 复制代码
for(int i = 0; i < stones.length(); i++){
    if(jew.contains(stones.charAt(i))) hasJews++;
}

这里的关键是:ArrayList.contains(x) 内部是线性扫描

它会从头到尾挨个比较,直到找到 x 或扫完整个列表才停。

复杂度

  • 建 list:O(|jewels|)
  • 遍历 stones:每次 contains 是 O(|jewels|)
  • 总体:O(|stones| * |jewels|)
  • 空间:O(|jewels|)

在本题数据范围(最多 50)时,体感差别不大;但如果把这个思路迁移到更大数据规模,这种写法会明显慢。


4. 两种写法对比:为什么 HashSet 更推荐?

4.1 成员判断的时间复杂度差距

  • HashSet.contains:平均 O(1)
  • ArrayList.containsO(m)(m = jewels 长度)

这题的核心操作就是"判断某个字符是不是宝石",判断次数是 |stones| 次。

把每次判断从 O(m) 优化到 O(1),整体就从乘法变加法。

4.2 语义表达更贴题

  • jewels 本质是"一个集合",用 Set 表达非常自然
  • List 更像"有序容器",但这里根本不需要顺序

4.3 可扩展性更强

今天 jewelsstones 长度最多 50;

如果换成长度 10 万的字符串,List 版本会立刻吃不消,而 Set 版本仍然稳。


5. 小细节:为什么 Character 也能放进 HashSet?

因为 Java 会把 char 自动装箱成 Character 对象(autoboxing)。
HashSet<Character> 存的是对象,但写 set.add(jewels.charAt(i)) 依然很自然。


总结

这题最直接的建模就是:

  • jewels 是"宝石类型集合"
  • stones 是"待检测序列"
  • 遍历 stones,遇到属于集合的就计数

因此:

  • HashSet :成员判断快,复杂度 O(n + m),表达也贴题
  • List :成员判断要线性扫,复杂度 O(n*m),能做对但不够优雅

在面试或工程里,我会优先选择 HashSet 版本:它不仅更快,而且把"宝石种类是集合"这个事实写进了代码结构里,读起来也更像在讲人话。

相关推荐
·薯条大王1 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
To_OC2 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
白狐_7982 小时前
408数据结构第5章:二叉树遍历序列题——技巧、判断与真题型总结
数据结构
ZJH__GO4 小时前
网络编程v4pro--实现聊天室文件传输功能
java·服务器·网络·计算机网络
程序员黑豆4 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
命运之光4 小时前
【C语言完整代码】就诊信息管理系统
java·c语言·开发语言
凤山老林4 小时前
Spring Boot @Async 线上实战:从默认配置到生产级线程池治理
java·spring boot·后端
Forever Nore5 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
marvelyu5 小时前
每天10分钟学会OceanBase系列(Day 20):跨机房容灾实战——构建多数据中心高可用架构
java·大数据·数据库
AI人工智能+电脑小能手6 小时前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦