哈希表
基本知识
| 集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
|---|---|---|---|---|---|---|
| std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
| std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
| std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
| 映射 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
|---|---|---|---|---|---|---|
| std::map | 红黑树 | key有序 | key不可重复 | key不可修改 | O(logn) | O(logn) |
| std::multimap | 红黑树 | key有序 | key可重复 | key不可修改 | O(log n) | O(log n) |
| std::unordered_map | 哈希表 | key无序 | key不可重复 | key不可修改 | O(1) | O(1) |
| 判断一个元素是否出现过的场景也应该第一时间想到哈希法! |
242.有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
题解:26个字母计数即可
383.赎金信
与242类似,以第二个字符串为备选,判断是否可以填满第一个字符串
49.字母异位词分组
unordered_map的key必须是可以hash的,官方兼容了基本数据类型的hash(如 int, string, char, size_t)
官方写法把array<int,26>即统计每个字母有多少个的数组,用一个hash表示,然后将该hash作为unordered_map的key来索引每个异位词。
有一个简便写法,可以自己制作hash,例如用“a2b1”来表示“aab”,string是可以作为索引的。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> map;
for(string str:strs){
int cnts[26]={0};
for(char c:str){
cnts[c-'a']++;
}
string key="";
for(int i=0;i<26;i++){
if(cnts[i]!=0){//这个判断不必须,单纯剩空间
key.push_back(i+'a');//字符串拼接可以直接+,但在当前语义会有歧义所以用push_back
//但push_back只能添加一位字符,所以要保证添加的字符ASCII码在(0~127)之间
key+=to_string(cnts[i]);//所以这里直接使用+=,因为若该字符数量大于127则会报错
}
}
map[key].push_back(str);
}
vector<vector<string>> res;
for(auto& i:map){
res.push_back(i.second);
}
return res;
}
};
官方代码
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;
}
};
string的基本用法
一、构造
std::string s1 = "Hello"; // 从字符串字面量构造
std::string s2("World"); // 构造函数形式
std::string s3(5, 'a'); // 重复字符 'a' 5 次 -> "aaaaa"
std::string s4 = s1 + " " + s2; // 字符串拼接
string s(1, ascii_code); // 构建单个ASCII码的string
二、常用操作
1. 获取长度
s1.length(); // 或 s1.size();
2. 拼接字符串
std::string s = "Hello";
s += " World"; // => "Hello World"
s.append("!"); // => "Hello World!"
s.push_back(i+'a'); //防止使用ASCII码时产生歧义,但是只能添加单个字符
3. 子串提取
std::string sub = s.substr(0, 5); // 从下标0开始,取5个字符 => "Hello"
4. 查找字符串或字符
size_t pos = s.find("World"); // 返回起始下标,找不到返回 string::npos
if (pos != std::string::npos) {
std::cout << "Found at " << pos;
}
5. 替换子串
s.replace(6, 5, "C++"); // 从下标6开始,替换5个字符为"C++" => "Hello C++!"
6. 插入和删除
s.insert(5, ","); // 在下标5插入","
s.erase(5, 1); // 从下标5开始删除1个字符
三、字符访问
char c = s[0]; // 直接访问,不做越界检查
char d = s.at(1); // 有越界检查(抛异常)
// 遍历字符串每个字符
for (char ch : s) {
std::cout << ch;
}
四、字符串和数字转换(C++11 起)
string -> int / double
std::string s = "123";
int x = std::stoi(s); // 字符串转 int
double d = std::stod("3.14");
int / double -> string
int a = 42;
std::string s = std::to_string(a); // => "42"
1002. 查找常用字符
给你一个字符串数组 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] 由小写英文字母组成
题解,将每个字符串中字符出现频率统计出来算最小值即可
vector容器中emplace_back 和 push_back 的区别
emplace_back省略了构造变量的步骤
ans.push_back(std::string(1, 'c'));
ans.emplace_back(1, 'c'); // 推荐:直接构造,简洁高效

浙公网安备 33010602011771号