LeetCode随缘刷题之Java经典面试题将一个字符串数组进行分组输出,每组中的字符串都由相同的字符组成

今天给大家分享一个Java经典的面试题,题目是这样的:
本题是LeetCode题库中的49题。

将一个字符串数组进行分组输出,每组中的字符串都由相同的字符组成
举个例子:输入[“eat”,“tea”,“tan”,“ate”,“nat”,“bat”]
输出[[“ate”,“eat”,“tea”],[“nat”,“tan”],[“bat”]]

刚见到这个题,我的第一反应是正则。通过正则表达式加上循环,来筛选判断两个字母是否全部由相同字符组成。结果发现并没有想象中的简单。

于是我转换思路,想到用数组的方式来求,即将每个元素都转化为char类型数组,先判断长度是否相等。然后比较,也就是下面StringArrays类中的compareReverse方法。
但是这样效率实在不高,而且还要循环一次。因为会有包含关系的出现(比如abc就包含abb),这就需要我们再反向调用一次,这样才得到了最终方法compare
后来我想到用排序也可以实现啊,将字符串拆分排序后再合并,如果两个字符串相同,那肯定都由相同的字符组成。于是就有了2.0版本compareNow:

package day_12_02.zuoye;

import java.util.Arrays;

/**
 * @author soberw
 */

public class StringArrays {

    /**
     * 给定两个字符串,判定第二个串中的字符是否在第一个字符串中全部存在
     * contains()当且仅当此字符串包含指定的char值序列时才返回true。
     *
     * @param a 第一个字符串
     * @param b 第二个字符串
     * @return boolean
     */
    private boolean compareReverse(String a, String b) {
        //记录次数
        int count = 0;
        char[] newB = b.toCharArray();
        for (char bb : newB) {
            if (a.contains(String.valueOf(bb))) {
                count++;
            }
        }
        //相等说明全部包含在内
        if (count == a.length()) {
            return true;
        }

        return false;
    }

    /**
     * 对字符串排序
     *
     * @param str 字符串
     * @return 排序后的
     */
    private String sortString(String str) {
        char[] s = str.toCharArray();
        Arrays.sort(s);
        return String.valueOf(s);
    }

    /**
     * 判断两个字符串是否由相同字符组成,切长度相同
     *
     * @param m 第一个字符串
     * @param n 第二个字符串
     * @return boolean
     */
    public boolean compare(String m, String n) {
        if (m.length() != n.length()) {
            return false;
        }
        //避免出现子包含情况
        return compareReverse(m, n) && compareReverse(n, m);
    }

    /**
     * 对compare方法的改进
     *
     * @param m 第一个字符串
     * @param n 第二个字符串
     * @return boolean
     */
    public boolean compareNow(String m, String n) {
        if (m.length() != n.length()) {
            return false;
        }
        return sortString(m).equals(sortString(n));
    }

}

想法实现一

解决了比较问题,于是迎来了第一次测试,一开始我只想到走二维数组来实现,加双重循环判断放入。

package day_12_02.zuoye;

import java.util.Arrays;

/**
 * 对StringArrays的测试
 *
 * @author soberw
 */
public class SATest {
    public static void main(String[] args) {
        StringArrays sa = new StringArrays();
        String[] str = {"eat", "tea", "tan", "ate", "nat", "bat"};
        String[][] newStr = new String[str.length][str.length];
        for (int i = 0; i < str.length; i++) {
            for (int j = 0; j < str.length; j++) {
                if (sa.compare(str[i], str[j])) {
                    newStr[i][j] = str[j];
                }
            }
        }
        for (String[] s : newStr) {
            System.out.println(Arrays.toString(s));
        }
    }
}

结果是这样的 😦 我直接凌乱。。。(虽然真的分好组了)
在这里插入图片描述
想法不成立。

其实原因很简单,数组局限性太多,必须指定长度,而且会有默认初始值null,当然最后可以去重得到最终结果。但是我实在是嫌麻烦(二维数组去重…)于是果断更换思路。

想法实现二

我们都知道数组是指定长度的,那有没有不指定长度的动态的呢?当然,Java给我们提供了很多,如set,map,list,vector等等。 目前我的想法是创建一个动态的二维数组,和一个临时数组。通过循环,先取出一个单词放在临时数组中,并将原数组该位置值变为null,然后拿临时数组的元素和原数组的所有非null元素比较(通过compareNow方法),匹配一个放入一个,并将原数组对应元素赋值为null,循环的最后将临时数组放入二维数组中,依次循环下去,直到原数组值全部变为null。

下面实施,我决定选用Vector来存放数据:

package day_12_02.zuoye;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;

/**
 * 对StringArrays的测试
 *
 * @author soberw
 */
public class StringArr {

    public static void main(String[] args) {
        StringArrays sa = new StringArrays();
        //初始数组
        Vector<String> v = new Vector<>(List.of("eat", "tea", "tan", "ate", "nat", "bat"));
        //保存数据
        Vector<Vector<String>> saveV = new Vector<>();
        //暂存数组
        Vector<String> ver = new Vector<>();
        while (true) {
           //开始时清空
            ver.clear();
            for (int j = 0; j < v.size(); j++) {
                if (v.get(j) != null) {
                    ver.add(v.get(j));
                    v.set(j, null);
                    break;
                }
            }
            if (ver.size() == 0) {
                break;
            }
            for (int k = 0; k < v.size(); k++) {
                if (v.get(k) != null) {
                    if (sa.compareNow(ver.get(0), v.get(k))) {
                        ver.add(v.get(k));
                        v.set(k, null);
                    }
                }
            }
            System.out.println(ver);
            saveV.add(ver);
        }
        System.out.println(Arrays.toString(saveV.toArray()));
    }
}

运行结果:
在这里插入图片描述
这是怎么回事,为什么上面输出正确,最后却为空呢。想一想,我终于明白为什么了。saveV.add(ver);这段代码只是添加了ver变量的引用,也就是说ver和saveV的数据都在同一地址单元内。而我每次循环开始都ver.clear() 将ver内的数据清空,那么对应的saveV中的数据也被清空了。
那么如何解决呢,很简单,每次循环重新new一个数组就好了:

package day_12_02.zuoye;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;

/**
 * 对StringArrays的测试
 *
 * @author soberw
 */
public class StringArr {

    public static void main(String[] args) {
        StringArrays sa = new StringArrays();
        //初始数组
        Vector<String> v = new Vector<>(List.of("eat", "tea", "tan", "ate", "nat", "bat"));
        //保存数据
        Vector<Vector<String>> saveV = new Vector<>();
        while (true) {
            //暂存数组
            Vector<String> ver = new Vector<>();
            for (int j = 0; j < v.size(); j++) {
                if (v.get(j) != null) {
                    ver.add(v.get(j));
                    v.set(j, null);
                    break;
                }
            }
            if (ver.size() == 0) {
                break;
            }
            for (int k = 0; k < v.size(); k++) {
                if (v.get(k) != null) {
                    if (sa.compareNow(ver.get(0), v.get(k))) {
                        ver.add(v.get(k));
                        v.set(k, null);
                    }
                }
            }
            System.out.println(ver);
            saveV.add(ver);
        }
        System.out.println(Arrays.toString(saveV.toArray()));
    }
}

结果完美找出:
在这里插入图片描述
想法成立。

想法实现三

那么这就是最好的解决方案了吗?我认为当然不是。虽然实现了但我觉得还是太麻烦。还要声明函数,还要加循环判断,还有反复声明数组存放,如果面试时这样写,先不说面试官看了直摇头,就这代码量和需要考虑的点,等你还在改bug呢,其他同学早就交卷了。。。😦

于是催生出了想法三:

充分利用集合的不重复性,先将愿数组所有元素字符排序,放入集合,这样集合就能筛选出所有重复的。
然后只需要把这个集合遍历一遍,与原数组所有元素比较,相同则放入一个临时集合中。最后统一保存在二维集合中去。

开始实施:

package day_12_02.zuoye;


import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author soberw
 */

public class StrArr {
    /**
     * 对字符串排序
     *
     * @param str 字符串
     * @return 排序后的
     */
    private static String sortStr(String str) {
        char[] s = str.toCharArray();
        Arrays.sort(s);
        return String.valueOf(s);
    }

    public static void main(String[] args) {
        //原始数组
        String[] str = {"ate", "tae", "tan", "ant", "eat", "bat"};
        //中间集合
        Set<String> set = new HashSet<>();
        //存放最终结果
        Set<Set<String>> setLast = new HashSet<>();
        for (int i = 0; i < str.length; i++) {
            set.add(sortStr(str[i]));
        }
        for (String s : set) {
            //暂存数据
            Set<String> seter = new HashSet<>();
            //排序后相等的元素放入临时集合
            for (int i = 0; i < str.length; i++) {
                if (s.equals(sortStr(str[i]))) {
                    seter.add(str[i]);
                }
            }
            setLast.add(seter);
        }
        System.out.println(setLast);
    }
}

运行结果:
在这里插入图片描述
想法成立。

想法实现四

可以看到代码量惊人的减少了,但想法三中设置的中间集合让我灵光一现。我们都知道数组是通过索引下标来走的:0,1,2,3…索引是不可控的,我想着能不能控制索引,将索引的值改为将要判断的值呢?这样就不用通过中间集合了,可以直接通过索引值调用元素了。于是我想到了,Java给我们提供了Map,你可以理解为键值对的一种集合。

实现如下:

package day_12_02.zuoye;

import java.util.*;

/**
 * @author soberw
 */
public class SATrue {
    public static void main(String[] args) {
        String[] strs = {"ate", "tae", "tan", "ant", "eat","bat"};
        System.out.println(new SATrue().grouper(strs));
    }

    public List<List<String>> grouper(String[] strs) {
        if (strs == null || strs.length == 0) {
            return new ArrayList<>();
        }
        Arrays.sort(strs);
        Map<String, List<String>> map = new HashMap<>();
        for (String str : strs) {
            char[] c = str.toCharArray();
            Arrays.sort(c);
            String sortedStr = String.valueOf(c);
            if (!map.containsKey(sortedStr)) {
                map.put(sortedStr, new ArrayList<>());
            }
            map.get(sortedStr).add(str);
        }
        return new ArrayList<>(map.values());
    }
}

运行结果:
在这里插入图片描述
想法成立。

相比于想法三又简化了一步,这是我目前能够想到的最优解了。

一个看似简单的题目,实现起来却并不简单。刚开始我以为本题难点在于筛选出相同的字符组成的元素,结果等真正实现却发现,难点在于如何把它们表现在二维数组中,即如何分组显示。

如果你们有更好的想法,欢迎评论区留言。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值