0

0

LeetCode无排序分组变位词:基于字符频率的Java实现与复杂度分析

DDD

DDD

发布时间:2025-10-14 12:32:22

|

876人浏览过

|

来源于php中文网

原创

LeetCode无排序分组变位词:基于字符频率的Java实现与复杂度分析

本文详细探讨了一种在leetcode中无需排序即可对字符串数组进行变位词分组的java解决方案。该方法巧妙地利用字符频率数组作为hashmap的键,将具有相同字符组成(即变位词)的字符串高效地归类。文章深入解析了`arrays.tostring()`在此过程中的关键作用,并提供了详细的代码实现及严谨的时间复杂度`o(m*n)`分析,其中`m`为字符串数量,`n`为平均字符串长度。

变位词分组的核心思想

变位词(Anagrams)是指由相同字母以不同顺序排列而成的单词或短语,例如 "eat", "tea", "ate" 都是彼此的变位词。传统的变位词判断方法通常涉及对字符串进行排序,然后比较排序后的结果。然而,这种方法对于每个字符串都需要O(N log N)的时间复杂度。为了优化性能,我们可以采用一种无需排序的方法:字符频率计数

其核心思想是:如果两个字符串是变位词,那么它们包含的每个字符的数量必然是相同的。例如,"eat" 和 "tea" 都包含一个 'e',一个 'a',一个 't'。因此,我们可以为每个字符串构建一个字符频率“指纹”,并使用这个指纹作为哈希表的键来将变位词分组。

基于字符频率数组的实现

在Java中,我们可以使用一个长度为26的整型数组来记录每个小写英文字母的出现次数。数组的每个索引对应一个字母(0对应'a',1对应'b',以此类推)。

  1. 构建频率数组:对于输入数组中的每个字符串str,初始化一个int[26]的数组input。遍历str中的每个字符c,通过input[c - 'a']++来增加对应字符的计数。 例如,对于字符串 "eat":

    • input['e' - 'a'] 增加 1
    • input['a' - 'a'] 增加 1
    • input['t' - 'a'] 增加 1 最终,input 数组可能看起来像 [1, 0, 0, 0, 1, 0, ..., 1, ..., 0],其中 'a', 'e', 't' 对应的位置是 1,其余为 0。
  2. 生成哈希键:关键在于如何将这个int[26]数组转换为一个可以作为HashMap键的唯一标识。Arrays.toString(input) 方法在此发挥了巧妙的作用。它会将整型数组转换成一个字符串表示,例如 "[1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]"。 这个字符串是唯一且一致的,因为:

    • 任何变位词(如 "eat", "tea", "ate")都会生成完全相同的字符频率数组。
    • Arrays.toString() 会将相同的数组内容转换成相同的字符串。 因此,这个字符串就成为了变位词组的理想哈希键。
  3. 分组存储:使用一个HashMap>来存储分组结果。键是上述生成的频率数组字符串,值是一个List,包含所有具有该频率模式的原始字符串。

    立即学习Java免费学习笔记(深入)”;

    • 如果outputMap中已存在该频率字符串作为键,则将当前字符串添加到对应的List中。
    • 如果不存在,则创建一个新的List,将当前字符串添加进去,并将该频率字符串作为键,新List作为值存入outputMap。
  4. 收集结果:遍历完所有输入字符串后,outputMap中的所有值(即List)就是最终的分组结果。将其收集到一个List>中返回。

示例代码

以下是该方法的Java实现:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AnagramGrouper {

    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> output = new ArrayList<>();
        if (strs == null || strs.length == 0) {
            return output;
        }

        Map<String, List<String>> outputMap = new HashMap<>();

        for (String str : strs) {
            // 1. 构建字符频率数组
            int[] charCounts = new int[26]; // 26个小写英文字母
            for (char c : str.toCharArray()) {
                charCounts[c - 'a']++;
            }

            // 2. 将频率数组转换为字符串作为HashMap的键
            String frequencyKey = Arrays.toString(charCounts);

            // 3. 根据键进行分组存储
            if (outputMap.containsKey(frequencyKey)) {
                outputMap.get(frequencyKey).add(str);
            } else {
                List<String> anagramList = new ArrayList<>();
                anagramList.add(str);
                outputMap.put(frequencyKey, anagramList);
            }
        }

        // 4. 收集所有分组结果
        output.addAll(outputMap.values());
        return output;
    }

    public static void main(String[] args) {
        AnagramGrouper grouper = new AnagramGrouper();
        String[] strs1 = {"eat", "tea", "tan", "ate", "nat", "bat"};
        List<List<String>> result1 = grouper.groupAnagrams(strs1);
        System.out.println("示例1分组结果: " + result1);
        // 预期输出可能为: [[bat], [nat, tan], [eat, tea, ate]] (顺序可能不同)

        String[] strs2 = {""};
        List<List<String>> result2 = grouper.groupAnagrams(strs2);
        System.out.println("示例2分组结果: " + result2); // 预期输出: [[]]

        String[] strs3 = {"a"};
        List<List<String>> result3 = grouper.groupAnagrams(strs3);
        System.out.println("示例3分组结果: " + result3); // 预期输出: [[a]]
    }
}

时间复杂度分析

该算法的时间复杂度可以按以下步骤进行分析:

Sesame AI
Sesame AI

一款开创性的语音AI伴侣,具备先进的自然对话能力和独特个性。

下载
  1. 外层循环:遍历输入字符串数组strs,假设数组中有m个字符串。这一步的复杂度是O(m)。

  2. 内层循环(字符计数):对于每个字符串str,我们都需要遍历其所有字符来构建频率数组。假设字符串的平均长度为n。那么,对于每个字符串,此操作的复杂度是O(n)。

  3. Arrays.toString():将长度为26的int数组转换为字符串。由于数组长度是固定的常数26,此操作的复杂度是O(1)(或者更精确地说,O(26),可以简化为O(1))。

  4. HashMap操作:containsKey(), get(), put(), add()等操作在平均情况下都具有O(1)的时间复杂度。在最坏情况下(哈希冲突严重),可能退化到O(k),其中k是键的长度。在这里,我们的键是一个字符串,其长度也是固定的常数(Arrays.toString()生成的字符串长度固定,与26个字符的数组相关)。因此,这些操作可以视为O(1)。

综合以上分析,总时间复杂度为:

  • 外层循环执行m次。
  • 每次外层循环中,执行O(n)的字符计数操作。
  • 每次外层循环中,执行O(1)的Arrays.toString()操作。
  • 每次外层循环中,执行O(1)的HashMap和ArrayList操作。

因此,总时间复杂度可以表示为 m * (n + 1 + 1),即 O(m * n + 2m)。当m和n足够大时,常数项和较低次项可以忽略,最终的时间复杂度为 *`O(m n)`**。

注意事项与总结

  • 字符集限制:此方案假设输入字符串只包含小写英文字母。如果包含大写字母、数字或其他字符,需要调整频率数组的大小(例如,使用128或256来覆盖ASCII码)或使用HashMap来存储频率。
  • 空间复杂度:空间复杂度主要取决于HashMap存储的键值对数量。最坏情况下,所有字符串都不是变位词,HashMap会存储m个键,每个键是一个字符串(长度固定),每个值是一个List。因此,空间复杂度为O(m * k),其中k是键的长度,可以简化为O(m)。此外,每个List会存储原始字符串,因此还需要O(总字符数)的空间。
  • 性能优势:相较于对每个字符串进行排序的方法,这种基于频率计数的方法避免了对字符串内部进行比较排序的O(N log N)开销,在许多情况下能够提供更好的性能。

通过巧妙地利用字符频率数组和Arrays.toString()方法,我们能够高效地解决变位词分组问题,为处理大量字符串数据提供了一个优化方案。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1010

2023.08.02

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1566

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

649

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

1228

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

1184

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

192

2025.07.29

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号