文章目录
题目
定义
当两个单词的各个字母数量相等时,称两个词在一组Anagram(比如“eat”和“ate”, 再或者“stop”,“spot”,和“tops”)
目的
给一串数据结构为ArrayList<String>的字典(dictionary), 每个String代表一个词(且不区分大小写)。
输出一串数据结构为ArrayList<ArrayList<String>>的答案,每个在ArrayList中的ArrayList<String>代表一组anagram。
解题思路
为了能尽可能地降低算法所需时间,我们可以先分析必须要干什么。
首先我们肯定要遍历整个字典,去得到每个词。时间复杂度 O(n)n为字典长度
其次,词的每个字母我们也都要过一遍(从而能知道这个词能不能被放到已知的anagram组里。)时间复杂度 O(n*k) k为字典中词的平均长度
将词判断并插入进对的位置 时间复杂度 未知
最后输出重新排序分类的anagram组群
难点
- 遍历字母后,如何判断应该去往那个anagram的组
- 判断完,如何能用最快的速度插入这个词到对应位置
倘若这两个过程能在O(n),我们就能将算法整体时间压缩到O(n)+ O(n*k)+ O(n), 也就是O(n)
方法
CountingSort:
其特点是在有限种类元素构成的数组上,能在O(n)时间内将数组排序 (我们归纳的单词都在26个字母之内加空格)
Hashmap:
其特点在于插入信息和找寻信息都是O(1)的时间复杂度,所以很适合用在这里。
所以思路整体是当我们得到每一个单词后,先将单词按照字母顺序(a小z大)排序。之后hash掉排序后的单词,生成一个id和一个key,将原单词按照key的位置放进去。(id是当哈希碰撞发生时,可以用来分辨单词应对应哪组anagram)最后遍历map,输出所有结果。
实现代码
整体架构
先将整体代码思路写下来,再实现 Hashmap 和 countingSort 的部分
public ArrayList<ArrayList<String>> getAnagrams(ArrayList<String> dictionary){
ArrayList<ArrayList<String>> anagrams = new ArrayList<ArrayList<String>>(); //创建输出结果
Hashmap map = new Hashmap(dictionary.size()); //根据输入字典大小,创建Hashmap
for (String str : dictionary) {
//遍历字典
char[] cur = str.toCharArray(); //将词拆成字母的数组
countingSort(cur); //将数组重新排序
if (!map.containsKey(cur)) {
//判断数组所表示的词是否有相应的anagram组
map.put(cur, str); //没有就创建一个用put
} else {
map.add(cur, str); //有就在对应的anagram组将词加进去
}
}
map.toArray(anagrams); //遍历Hashmap将结果加进去
return anagrams; //输出答案
}
然后开始逐一实现 CountingSort 和 Hashmap。
计数排序(CountingSort)
计数排序其实就是遍历数组,数每个字符(字母)出现了多少次&#