题目介绍:

方法1:遍历法
cpp
#include <algorithm>
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs)
{
vector<string> beg(strs);
for (string& k : beg)
sort(k.begin(), k.end()); // 排序作为键
vector<vector<string>> rel;
vector<bool> visited(strs.size(), true); // 使用visited标记
for(int i=0;i<beg.size();i++)
{
vector<string> temp;
if(visited[i]==true)
{
temp.push_back(strs[i]);
for(int j=i+1;j<beg.size();j++)
{
if(beg[i]==beg[j])
{
temp.push_back(strs[j]);
visited[j]=false;
}
}
rel.push_back(temp);
}
}
return rel;
}
};
(博主自己敲的方法,不推荐,适合理解)
代码思路
-
创建beg数组,是strs的拷贝,然后对beg中的每个字符串进行排序(这样同一个变位词组排序后字符串相同)。
-
然后遍历beg数组,对于每个字符串,如果它是true,则创建一个临时组,将当前字符串加入(注意:这里加入的是排序后的字符串,但实际需要的是原字符串)。 3. 然后内层循环从i+1开始,找后面所有与beg[i]相同的字符串,将对应的原字符串(strs[j])加入临时组,并将beg[j]标记为false(表示已经处理过)。
-
最后将临时组加入结果rel中。
方法2:哈希桶
cpp
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs)
{
//哈希桶
unordered_map<string,vector<string>> mp;//创建一个字符串和字符串数组对应键值的哈希表
for(auto str:strs)//迭代器将数组里的字符串全部排序,然后在对应的字符串键处存放原字符串
{
string key=str;
sort(key.begin(),key.end());
mp[key].push_back(str);//这里如果用emplace_back内存消耗更少
}
vector<vector<string>> rel;
for(auto it=mp.begin();it!=mp.end();it++)//遍历哈希桶
{
rel.push_back(it->second);//将字符串组插入rel中,这里如果用emplace_back内存消耗更少
}
return rel;
}
};
复杂度分析
1.时间复杂度 :O(nklogk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要遍历 n 个字符串,对于每个字符串,需要 O(klogk) 的时间进行排序以及 O(1) 的时间更新哈希表,因此总时间复杂度是 O(nklogk)。
2.空间复杂度 :O(nk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要用哈希表存储全部字符串。
核心区别
特性 | push_back() |
emplace_back() |
---|---|---|
工作原理 | 构造临时对象 → 拷贝/移动到容器 | 直接在容器内存中构造对象 |
参数 | 接受对象本身 | 接受对象的构造参数 |
效率 | 可能额外构造临时对象(有拷贝开销) | 避免临时对象(更高效) |
适用场景 | 添加已存在的对象 | 直接构造新对象 |
这个方法不同的是不需要根据每个的字符串往后遍历,而是直接放进哈希桶,最后把vector<string>提出来就行。
力扣上还有个计数的方法比较复杂,但是时间复杂度和空间复杂度进一步降低了:
方法3:计数
cpp
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
// 自定义对 array<int, 26> 类型的哈希函数
auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
return (acc << 1) ^ fn(num);
});
};
unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
for (string& str: strs) {
array<int, 26> counts{};
int length = str.length();
for (int i = 0; i < length; ++i) {
counts[str[i] - 'a'] ++;
}
mp[counts].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
复杂度分析
1.时间复杂度:O(n(k+∣Σ∣)),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度,Σ 是字符集,在本题中字符集为所有小写字母,∣Σ∣=26。需要遍历 n 个字符串,对于每个字符串,需要 O(k) 的时间计算每个字母出现的次数,O(∣Σ∣) 的时间生成哈希表的键,以及 O(1) 的时间更新哈希表,因此总时间复杂度是 O(n(k+∣Σ∣))。
2.空间复杂度:O(n(k+∣Σ∣)),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的最大长度,Σ 是字符集,在本题中字符集为所有小写字母,∣Σ∣=26。需要用哈希表存储全部字符串,而记录每个字符串中每个字母出现次数的数组需要的空间为 O(∣Σ∣),在渐进意义下小于 O(n(k+∣Σ∣)),可以忽略不计。
喜欢的话就关注我吧~~~求关注求赞求收藏~~~^-^