查找共用字符
原题
给你一个字符串数组 words
,请你找出所有在 words
的每个字符串中都出现的共用字符(包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
示例 1:
输入:words = ["bella","label","roller"]
输出:["e","l","l"]
示例 2:
输入:words = ["cool","lock","cook"]
输出:["c","o"]
提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i]
由小写英文字母组成
class Solution {
public List<String> commonChars(String[] words) {
int[] countLetters = int[26];
}
}
解题思路
涉及到「快速判断一个元素是否已经存在」的场景,考虑哈希法:
- 创建
HashMap
来存储每个字符在所有字符串中出现的最小次数。 - 遍历第一个字符串,统计其中每个字符出现的次数并存储在
HashMap
中。 - 遍历剩余的字符串,更新每个字符的最小次数,即取每个字符在当前字符串和之前字符串中出现次数的最小值,同时移除不共同的字符。
- 最后将
HashMap
中存在的字符根据次数添加到结果列表中,并返回结果列表。
代码示例
class Solution {
public List<String> commonChars(String[] words) {
List<String> result = new ArrayList<>();
// 记录第一个单词的字符出现次数
Map<Character, Integer> charCount = new HashMap<>();
// 初始化为第一个单词的字符频率
for (char c : words[0].toCharArray()) {
charCount.put(c, charCount.getOrDefault(c, 0) + 1);
}
// 遍历剩余单词
for (int i = 1; i < words.length; i++) {
Map<Character, Integer> currentCount = new HashMap<>();
// 统计当前单词中的字符出现次数
for (char c : words[i].toCharArray()) {
currentCount.put(c, currentCount.getOrDefault(c, 0) + 1);
}
// 更新 charCount
for (char c : new ArrayList<>(charCount.keySet())) {
if (currentCount.containsKey(c)) {
// 保留共同字符的最小出现次数
charCount.put(c, Math.min(charCount.get(c), currentCount.get(c)));
} else {
// 移除不共同的字符
charCount.remove(c);
}
}
}
// 将共同字符添加到结果列表中
for (char c : charCount.keySet()) {
for (int j = 0; j < charCount.get(c); j++) {
result.add(String.valueOf(c));
}
}
return result;
}
}
说明
Map<Character, Integer> currentCount = new HashMap<>();
在每次循环中都会创建一个新对象,当循环迭代结束时,这个对象将超出作用域并被标记为可回收的对象并被 JVM 回收。虽然最终不会累积,但每次都会有新开辟的内存空间被创建和释放,会有轻微的额外开销。
// ...
// 遍历剩余单词
for (int i = 1; i < words.length; i++) {
Map<Character, Integer> currentCount = new HashMap<>();
// 统计当前单词中的字符出现次数
// ...
// 更新 charCount
// ...
}
// ...
优化
思路
因为题目中处理的字符集是小写字母(共26个),所以可以使用一个长度为26的数组来存储字符的出现次数。这样可以减少 Map
的开销:
Map
需要额外存储键值对的信息,包括键和值本身,这会占用更多的内存。而数组只需要基于索引存储值,不需要额外存储键。- 在
Map
中,当需要查找或插入一个键值对时,通常需要进行哈希计算以找到对应的位置。 Map
是一个更复杂的数据结构,需要维护键值对之间的关系,可能会涉及到平衡树、哈希表等数据结构,这些维护的成本会比较高。
优化后代码
class Solution {
public List<String> commonChars(String[] words) {
List<String> result = new ArrayList<>();
// 记录第一个单词的字符出现次数
int[] charCount = new int[26];
// 初始化为第一个单词的字符频率
for (char c : words[0].toCharArray()) {
charCount[c - 'a']++;
}
// 遍历剩余单词
for (int i = 1; i < words.length; i++) {
int[] currentCount = new int[26];
// 统计当前单词中的字符出现次数
for (char c : words[i].toCharArray()) {
currentCount[c - 'a']++;
}
// 更新charCount,保留每个字符的最小出现次数
for (int j = 0; j < 26; j++) {
charCount[j] = Math.min(charCount[j], currentCount[j]);
}
}
// 将共同字符添加到结果列表中
for (int i = 0; i < 26; i++) {
for (int j = 0; j < charCount[i]; j++) {
result.add(String.valueOf((char) (i + 'a')));
}
}
return result;
}
}