leetcode热题49:字母异位词分组,竟然和leetcode1异曲同工??

那当然了,毕竟都用的是hashmap解决的()。

来看看这道题吧:

1.分析题干:什么是字母异位词?

首先分析一下题干:字母异位词 是由重新排列源单词的所有字母得到的一个新单词。听着人都晕了,别看他说的好像很高大尚,其实内核很简单:举个例子,abc,acb,bac这三个词就是字母异位词,因为他们字母种类和个数都相同。也就是说,你可以这么理解:字母异位词就是 字符集相同的词。

这里我自己瞎定义了一个概念,叫字符集。其实也好理解,就是单词里所有字母的集合嘛。但为了表示几个单词的字符集相同,它就应该是有序的。回到例子上就是,abc,acb,bac三个词的字符集都是abc。

把这个概念绕清楚,题目瞬间就简化了:就是让你把字符集相同的词分在一组,作为一个List.最后返回多个list组成的集合。

2.思路:为什么要使用hashmap,怎样合理使用hashmap?

如果这个热题没有打上hash的标签,我们怎么去想到使用hashmap?其实也很简单,思考一下这个题目,第一直觉应该是要这么做:遍历原字符串数组中的每一个字符串,如果之前已经遍历过同样的字符集,就应该找到那个字符集对应的list,并加入到这个list中。如果之前没有遍历过这样的字符集,就新建一个list,再加入到这个list中。

我们说的很简单,但怎样去获取一个字符串的字符集?(比如怎样把acb转化成字符集abc?)得到了这个字符集,我又怎么获取这个字符集对应的list?

1)怎样获取字符集

其实不难,使用排序就可以了嘛。自己手搓一个冒泡排序,或者使用Arrays工具类提供的sort方法,都能实现对字符数组的排序,也就转成了字符集。

比如以下就是一个冒泡排序获取字符串的字符集的实现:(冒泡排序就不做注释了)

/**
     * 定义一个方法,这个方法用于获取字符串的升序字符集
     * @param str 接收一个字符串
     * @return 返回对应的字符集
     */
    public String getCharSetOfStr(String str){
        if(str.length() == 0){
            return "";
        }
        char[] chars = str.toCharArray();
        char temp;
        for (int i = 0; i < chars.length - 1; i++) {
            for (int j = 0; j < chars.length - 1 - i; j++) {
                if (chars[j] > chars[j+1]){
                    temp = chars[j];
                    chars[j] = chars[j+1];
                    chars[j+1] = temp;
                }
            }
        }
        return new String(chars);

    }

然后官方的题解中就是直接采用了Arrays.sort,两种方法都可以,大家参考一下。

2)怎样获取字符集对应的List?

这里就引出了我们为什么要使用hashmap:因为是典型的key-value结构。以字符集作为key,对应list在结果集中的索引作为value,那么每当我们拿到一个字符串的字符集,是不是就直接可以通过hashmap.get(key)的方式拿到list在结果集中的索引,再通过索引拿到list,然后将当前字符串加入list?

那如果hashmap中没有这个字符集的key呢?那就说明当前字符串是这个字符集里第一个被遍历到的嘛。直接新建一个list,将字符串加入list,然后把这个字符集和list的索引加入到hashmap。

OK,你已经做出了这道题了!虽然只是version1版本。

代码如下:

/**
     * 所谓将字母异位词组合在一起,其实就是字符集相同的字符串放到一起
     * 先new 一个ArrayList<List<String>>存放结果集
     * 然后遍历原字符串数组中的每一个字符串,查找hashmap中是否存在同样的字符集
     * 如果存在,则将这个字符串加入到对应字符集所在的List<String>中
     * 字符集和字符集所在list的索引分别由hashmap中entry的key和value维护
     * 如果不存在,则结果集add一个新的list用于存储这个字符集对应的字母异位词,
       //并将这个字符集和list索引加入hashmap中
     * 感觉完美
     *
     * 怎么获取一个字符串的字符集,并保证字母异位词对应同样的字符集?
     * 我的想法是,ade,aed,dea这些字符串的字符集都是ade(按照字母升序排列)
     * 获取这个升序字符集的方法是,对原字符串转为charArray后进行冒泡排序
     */
    public List<List<String>> groupAnagrams(String[] strs) {
        //1.创建结果集和hashmap,hashmap的key存放字符集,value存放list索引
        ArrayList<List<String>> resList = new ArrayList<>();
        HashMap<String, Integer> hashMap = new HashMap<>();
        //2.遍历每一个字符串
        for (String str : strs) {
            //获取升序字符集
            String charSet = getCharSetOfStr(str);//这个方法在下面定义了
            //如果字符集在hashmap中存在
            if (hashMap.containsKey(charSet)){
                //找到这个字符集的List索引,将字符串加入
                Integer index = hashMap.get(charSet);
                resList.get(index).add(str);
                //然后直接遍历下一个字符串
                continue;
            }
            //如果字符集在hashmap中不存在,说明这是一个新的字符集;
            //创建一个新的list<String>用于存放这个字符集对应的字母异位词,
            //并将这个字符集保存到hashmap
            ArrayList<String> newList = new ArrayList<>();
            newList.add(str);
            resList.add(newList);//将新list加入结果集resList
            //获取newList在resList中的索引位置并放入hashmap
            hashMap.put(charSet,resList.size() - 1);
        }
        //3.返回resList
        return resList;
    }

    /**
     * 定义一个方法,这个方法用于获取字符串的升序字符集
     * @param str 接收一个字符串
     * @return 返回对应的字符集
     */
    public String getCharSetOfStr(String str){
        if(str.length() == 0){
            return "";
        }
        char[] chars = str.toCharArray();
        char temp;
        for (int i = 0; i < chars.length - 1; i++) {
            for (int j = 0; j < chars.length - 1 - i; j++) {
                if (chars[j] > chars[j+1]){
                    temp = chars[j];
                    chars[j] = chars[j+1];
                    chars[j+1] = temp;
                }
            }
        }
        return new String(chars);

    }

3.本题如何优化?

1)获取字符集时,排序优化

你可能已经发现了最大的优化点:冒泡排序。n2的时间复杂度实在太大。优化方法也很简单,像官方一样Arrays.sort就可以将n2时间复杂度降到nlogn

2)改变对“字符集”的定义

上面使用“字符集”这个说法是因为比较容易想到,也比较容易理解(我自己第一次也是这么做的),但反过来想想,这样做真的是最优吗?

首先排序几乎达到n2的时间复杂度,再次,本题虽然str[i].length最大只有100,但往大了走,如果字符串有500个字符,或者更大,那我们的字符集不是也要达到500个字符?

所以就出现了第二种方案:计数法。我们在开始时也提到了,abc,acb,bac这三个词是字母异位词,因为他们字母种类和个数都相同。那么我们是不是可以通过字母种类和数字来组成更简洁、一次遍历就能获得的“新字符集”?比如,abbc我们用a1b2c1表示,即字母+出现次数,这样也同样能达到字母异位词的字符集相同的效果。是不是有点豁然开朗的感觉。

代码如下:(截取自官方题解)

for (String str : strs) {
            int[] counts = new int[26];
            int length = str.length();
            for (int i = 0; i < length; i++) {
                //比如counts[0]就代表当前字符串中‘a’出现的次数
                counts[str.charAt(i) - 'a']++;
            }
            // 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 26; i++) {
                if (counts[i] != 0) {
                    sb.append((char) ('a' + i));
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();

这样获取字符集的时间复杂度就变为了n,存储空间也占用更小了。

剩下的思路和之前的一模一样,hashmap的使用都是类似的,只是获取字符集的方法和时间复杂度不同。顺带一提,官方题解中hashmap的value直接就是存放了list本身,而我在做的时候是存放了list对应的索引,大家可以自己选择合适的方法,或者评论下你的做法。

我们下次再见!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值