news 2026/5/1 8:07:03

【Hot100-Java中等】:字母异位词分组

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Hot100-Java中等】:字母异位词分组

在 LeetCode 的字符串题目中,“字母异位词” (Anagrams) 是一个非常高频的概念。这道第 49 题不仅考察了哈希表(HashMap)的应用,更是一个理解 Java 对象机制的绝佳案例。


1. 解决方案一:排序数组分类 (Sorting)

这是最符合直觉的解法。既然“异位词”的字母成分一样,那只要把它们按照字母顺序重新排列,长得就一模一样了。

核心逻辑

  1. 遍历每个字符串。

  2. 将其转化为字符数组并排序(如"tea"->"aet")。

  3. 以排序后的字符串作为 Key,原始字符串作为 Value 加入 Map。

代码实现

Java

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); // 关键步骤:使用内容构造 Key List<String> list = map.getOrDefault(key, new ArrayList<String>()); list.add(str); map.put(key, list); } return new ArrayList<>(map.values()); } }

复杂度分析

  • 时间复杂度

    • 是字符串的数量。

    • 是字符串的最大长度。

    • 排序每个字符串需要 $O(K \log K)$ 的时间。

  • 空间复杂度。我们需要存储所有字符串的内容。


2. 解决方案二:计数法 (Counting)

如果字符串特别长(很大),排序的可能会成为瓶颈。我们可以利用题目条件:“仅包含小写字母”。

核心逻辑

不排序,而是给字符串做“成分体检”。

  1. 用一个长度为 26 的数组int[26]统计每个字母出现的次数。

  2. 将这个统计结果转换成一个唯一的 Key。

    • 例如"eat"的统计结果是:a=1, e=1, t=1,其他为0。

    • 我们可以构造一个 Key 字符串,如"a1e1t1..."或者简单的利用 StringBuilder 拼接非零字符和次数。

  3. 放入 HashMap。

代码实现

Java

class Solution { public List<List<String>> groupAnagrams(String[] strs) { Map<String, List<String>> map = new HashMap<>(); for (String str : strs) { // 1. 统计频次 int[] counts = new int[26]; int length = str.length(); for (int i = 0; i < length; i++) { counts[str.charAt(i) - 'a']++; } // 2. 构造唯一 Key // 技巧:将字母和次数拼起来,例如 "a1e1t1" StringBuilder sb = new StringBuilder(); for (int i = 0; i < 26; i++) { if (counts[i] != 0) { sb.append((char) ('a' + i)); sb.append(counts[i]); } } String key = sb.toString(); // 3. 存入 Map List<String> list = map.getOrDefault(key, new ArrayList<String>()); list.add(str); map.put(key, list); } return new ArrayList<>(map.values()); } }

复杂度分析

  • 时间复杂度

    • 我们在的时间内统计频次。

    • (即 26)的时间内生成 Key。

    • 因为没有了排序,当很大时,这种方法比排序法更快。

  • 空间复杂度

3. 深度思考:为什么两种方法一种采用new String()初始化,另一种采用toString()?

1. 数组 (char[]) 的toString():它是“懒惰”的

在 Java 中,数组(无论是int[],char[]还是String[])虽然是对象,但它们没有重写(Override)Object类的默认toString()方法。

默认行为(Object.toString)是:

返回类名 + @ + 内存地址哈希值

这就是为什么你之前会得到[C@15db9742这种乱码。它告诉你“我是一个字符数组,我住在内存的这个地址”,但它不会告诉你数组里存了什么内容。

  • 比喻:你指着一个装满苹果的箱子问:“你是什么?”箱子回答:“我是编号 888 的箱子。”(它不告诉你里面有苹果)。

2.StringBuffer/StringBuildertoString():它是“勤奋”的

在方法二中,代码是这样的:

Java

StringBuffer sb = new StringBuffer(); // ... 循环 append 字符和数字 ... String key = sb.toString(); // 这里调用的是 StringBuffer 的 toString

StringBuffer(以及StringBuilder)是专门用来处理字符串的类。它们的源代码里重写toString()方法。

它们的行为是:

把缓冲区里存储的所有字符,拼接成一个真正的String对象并返回。

  • 比喻:你指着一张写满字的问:“你是什么?”纸回答:“我上面写着‘a1b2c3’。”(它直接告诉你内容)。


3. 直观对比

我们可以写一段测试代码来看看区别:

Java

char[] arr = {'a', 'b', 'c'}; StringBuffer sb = new StringBuffer(); sb.append('a').append('b').append('c'); // 情况 1:数组调用 System.out.println(arr.toString()); // 输出:[C@6bc7c054 (乱码/地址) -> ❌ 即使内容一样,不同数组地址不同,导致 HashMap 认为是不同 Key // 情况 2:StringBuffer 调用 System.out.println(sb.toString()); // 输出:abc (内容) -> ✅ 只要内容一样,生成的 String 就一样,HashMap 认为是同一个 Key

4. 方法二的巧妙之处

方法二(计数法)之所以能成功,是因为它手动把数组里的统计信息,转化成了一个唯一的字符串

  • 对于 "eat":

    1. 统计数组:a=1, e=1, t=1

    2. sb.append拼接过程:"a1","e1","t1"

    3. sb.toString()结果:"a1e1t1"

  • 对于 "tea":

    1. 统计数组:a=1, e=1, t=1

    2. sb.append拼接过程:"a1","e1","t1"(顺序是按 a-z 遍历的,所以拼接顺序固定)

    3. sb.toString()结果:"a1e1t1"

因为最后生成的都是字符串对象"a1e1t1",而 Java 的String类比较的是内容(Value),所以 HashMap 判定它们相等。

总结

对象类型调用 toString() 的结果能否作为 HashMap 的 Key 用于分组?
数组 (char[])内存地址(如[C@xx)❌ 不能 (内容相同但地址不同)
StringBuffer实际文本内容(如"abc")✅ 能 (内容相同即为相等)
String实际文本内容✅ 能

所以,方法一要想修复,必须显式地把char[]转为String(使用new String(arr)),而不能依赖数组自带的toString()

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 6:10:09

PyTorch-CUDA-v2.6镜像部署OCR识别系统实战案例

PyTorch-CUDA-v2.6镜像部署OCR识别系统实战案例 在智能文档处理、自动化办公和工业质检等场景中&#xff0c;光学字符识别&#xff08;OCR&#xff09;正从“辅助功能”演变为关键的生产力引擎。然而&#xff0c;许多团队在落地OCR系统时仍面临一个共同困境&#xff1a;模型明…

作者头像 李华
网站建设 2026/4/24 18:55:02

PyTorch-CUDA-v2.6镜像部署RAG检索增强生成系统实战

PyTorch-CUDA-v2.6镜像部署RAG检索增强生成系统实战 在当前大模型驱动的AI浪潮中&#xff0c;如何快速构建一个既能准确回答问题、又能实时调用最新知识的智能系统&#xff0c;已经成为企业与研究团队的核心诉求。传统的语言模型虽然生成能力强&#xff0c;但容易“一本正经地胡…

作者头像 李华
网站建设 2026/4/26 11:31:56

PyTorch-CUDA-v2.6镜像中实现梯度裁剪防止训练爆炸

PyTorch-CUDA-v2.6镜像中实现梯度裁剪防止训练爆炸 在深度学习模型日益复杂、参数量动辄上亿的今天&#xff0c;一个看似微小的技术细节——梯度值异常增大&#xff0c;却可能让数小时甚至数天的训练功亏一篑。你是否曾遇到过这样的场景&#xff1a;模型刚开始训练&#xff0c;…

作者头像 李华
网站建设 2026/4/25 9:36:02

PyTorch-CUDA-v2.6镜像中使用Optuna进行超参数搜索

PyTorch-CUDA-v2.6 镜像中集成 Optuna 实现高效超参数搜索 在深度学习项目开发过程中&#xff0c;一个常见的瓶颈并非模型设计本身&#xff0c;而是如何快速找到一组能让模型性能显著提升的超参数组合。更棘手的是&#xff0c;即便你找到了“好”的参数&#xff0c;换一台机器或…

作者头像 李华
网站建设 2026/5/1 2:44:19

内存管理:避免内存泄漏的方法

在 JavaScript 开发中&#xff0c;内存管理是一个至关重要的话题&#xff0c;合理的内存管理能够避免内存泄漏&#xff0c;提高应用程序的性能和稳定性。本文将深入探讨 JavaScript 中的内存管理机制&#xff0c;以及如何避免内存泄漏的发生。1. 内存管理基础 1.1 内存生命周期…

作者头像 李华
网站建设 2026/5/1 4:45:22

事件委托:优化事件处理性能

在前端开发中&#xff0c;事件处理是构建交互性页面的关键部分。然而&#xff0c;随着页面元素数量的增加和交互复杂度的提升&#xff0c;事件处理的性能问题逐渐凸显。事件委托作为一种有效的优化策略&#xff0c;可以显著提升事件处理的效率&#xff0c;减少内存占用。本文将…

作者头像 李华