LeetBook乐扣题库 49. 字母异位词分组

目录

一 题目描述

二 方法代码

说在前面的两个知识点

List list = map.getOrDefault(key, new ArrayList());

new ArrayList>(map.values());

方法一(我自己的方法,效率并不好,可以看第二个官方方法)

方法二(排序,力扣官方解法)

三 运行结果

​编辑原因分析:

1.键的复杂性和比较成本

2.排序成本

3.内存

4. HashMap 操作的效率

一 题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]

输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]

输出: [[""]]

示例 3:

输入: strs = ["a"]

输出: [["a"]]

二 方法代码

说在前面的两个知识点

  • List<String> list = map.getOrDefault(key, new ArrayList<String>());

首先执行map.get(key),从 map 中获取与 key 关联的值。如果 key 存在于 map 中,它将返回一个 List<String>。如果 key 不存在于 map 中,它将返回 null,然后创建一个空列表

如果我们不使用 getOrDefault,我们需要手动检查 map.get(key) 是否为 null,如果是,则需要初始化一个新的 ArrayListgetOrDefault 方法自动处理了这个问题,简化了逻辑。

  • new ArrayList<List<String>>(map.values());

map.values() 方法返回的是 HashMap 中所有值的集合(Collection)。这个集合包含了 HashMap 中所有与键关联的值。

这段代码的作用是:

  1. HashMap 中获取所有的值(即 List<String>)。
  2. 创建一个新的 ArrayList<List<String>>,并将 HashMap 的值集合 Collection<List<String>> 复制到这个新的 ArrayList 中。
  3. 最终得到了一个包含 HashMap 中所有值的 ArrayList
  • 方法一(我自己的方法,效率并不好,可以看第二个官方方法)

我的思路是这样的,我一开始看到了标签时哈希表,我就在想这个怎么能联系到哈希表,我首先想的是str作为key,那选择什么作为value。要找出包含字符相同的字符串,我很快就想到了ASCII,我一开始想的是表示ASCII的和作为value,但是一想a、d的ASCII和与b、c的ADSCII和一样,所以苦于怎么进一步改善

后面看到哈希表的value可以定义为列表,我就想到了用从小到大的ASCII列表表示字符顺序不同但是一致字符的字符串了,就有了以下方法,但是效率不高(原因见第三点)

这个排序的ASCII列表表示key,这样每遇到一个相同的key,就刷新value的list,把这个字符串加入到这个list中

    public List<List<String>> groupAnagrams(String[] strs) {
        // 注意map的key时列表,表示字符串的字母ASCII值的大小顺序
        Map<List<Integer>, List<String>> map = new HashMap<>();

        for (String str : strs) {
            // 用来按大小顺序存储每一个字符的ASCII值
            List<Integer> asciiOrder = new ArrayList<Integer>();
            for (char c : str.toCharArray()) {
                int num = 0;
                // num表示指针,从列表中第一个数开始,找到合适位置插入
                while (num < asciiOrder.size()) {
                    // (int)c表示拿去c的ASCII值
                    if ((int) c <= asciiOrder.get(num)) {
                        asciiOrder.add(num, (int) c);
                        break;
                    }
                    num++;
                }
                // 如果num等于长度,说明所有的数都小于c的ASCII值,直接插入到最后
                if (num == asciiOrder.size()) {
                    asciiOrder.add((int) c);
                }
                // getOrDefault会判断map中有没有asciiOrder这个key
                // 如果有,就把asciiOrder对应的列表赋值给list
                // 如果没有,就默认创建一个空列表list
                // 如果已经有这个asciiOrder的key时,就拿出其对应的列表,然后把str加入到后面
            }
            List<String> list = map.getOrDefault(asciiOrder, new ArrayList<>());
            list.add(str);
            map.put(asciiOrder, list);
        }
        return new ArrayList<List<String>>(map.values());
    }

  • 方法二(排序,力扣官方解法)

思路大致就是,把每一个字符串按照字符的ASCII值排序重新组为一个字符串,这个排序的字符串表示key,这样每遇到一个相同的key,就刷新value的list,把这个字符串加入到这个list中

  public List<List<String>> groupAnagrams1(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for(String str : strs){
            char[] chars = str.toCharArray();
            Arrays.sort(chars);
            String sorted = new String(chars);
            // getOrDefault会判断map中有没有sorted这个key
            // 如果有,就把sorted对应的列表赋值给list
            // 如果没有,就默认创建一个空列表list
            // 这样如果已经有这个sorted的key时,就拿出其对应的列表,然后把str加入到后面
            List<String> list = map.getOrDefault(sorted,new ArrayList<>());
            list.add(str);
            //HashMap会覆盖key对应的关系,也就是更新value即加入了当前str的list
            map.put(sorted,list);
        }
        return new ArrayList<List<String>>(map.values());
    }

三 运行结果

原因分析:

1.键的复杂性和比较成本

我自己的方法用一个列表做key,List是一个复杂对象,因此每次比较key时需要遍历列表,时间复杂度很高

排序方法用String做key,相对简单的对象,查找和比较效率很高

2.排序成本

我自己的方法通过手动插入排序的方式将字符的 ASCII 值按顺序插入到 asciiOrder 列表中。这种手动排序的方式效率较低,尤其是当字符串长度较大时,插入排序的时间复杂度接近 O(n²)。

排序方法调用 Arrays.sort(chars) 对字符数组进行排序。Arrays.sort 使用的是一种优化过的双轴快速排序(Dual-Pivot Quicksort)算法,时间复杂度为 O(n log n)。

3.内存

我的方法每次处理字符串时,会创建一个新的 List<Integer> 对象,并且需要动态调整和插入元素。这会增加内存使用和对象分配的开销。

排序方法只是创建了一个字符数组 char[],并对其进行排序。字符数组是一个基本数据类型的数组,因此内存使用更为高效,且不涉及对象包装的开销。

4. HashMap 操作的效率

我的方法,由于键是 List<Integer>HashMap 需要通过调用 ListhashCode() 方法生成哈希值,并通过 equals() 方法进行键比较。这些操作在处理复杂对象(如 List)时效率较低。

排序方法,键是 StringString 类的 hashCode()equals() 方法经过高度优化,能够快速计算哈希值和进行比较。因此,HashMap 的操作在这种情况下会更加高效。

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值