目录
1、unordered系列容器
在C++标准库中,unordered
系列容器是基于哈希表实现的,
用于存储唯一键(对于unordered_set
)或键值对(对于unordered_map
),
其中键具有唯一性。与map
和set
这类基于红黑树的容器相比,哈希表能够提供平均时间复杂度为O(1)的快速访问性能,不过在最坏情况下可能退化到O(n)。
主要的unordered
系列容器包括:
unordered_map
: 一个关联容器,包含键值对,键是唯一的。通过键可以快速访问对应的值。
unordered_multimap
: 一个关联容器,包含键值对,但键可以重复。即一个键可以对应多个值。
unordered_set
: 一个集合容器,包含唯一的元素。
unordered_multiset
: 一个集合容器,元素可以重复
主要特点:
无序性:这些容器中的元素不会按任何特定顺序存储,因而不能像有序容器那样进行范围查询。
快速访问:提供了非常快速的访问能力,平均情况下时间复杂度为O(1)。
自定义哈希函数:用户可以提供自定义的哈希函数来改善元素的分布,从而提升性能。
键的唯一性(对于
unordered_set
和unordered_map
):保证了每个键的唯一性。键的非唯一性(对于
unordered_multiset
和unordered_multimap
):允许键重复,实现一键多值的存储。
unordered
系列容器适用于以下场景:
当需要快速查找、插入和删除元素时。
当元素的顺序不重要时。
当不需要对元素进行范围查询时。
需要注意的是:
1、因为容器内部是无序的,所以遍历这些容器得到的元素顺序是不确定的。
2、当哈希表的负载因子(元素数量/桶数量)过高时,性能可能会下降。因此,适时地调整哈希表的大小是很重要的。
3、在使用自定义类型作为键时,需要确保提供有效的哈希函数和相等判断函数。
2、unordered系列容器OJ
1、重复n次的元素
给你一个整数数组 nums
,该数组具有以下属性:
nums.length == 2 * n
.nums
包含n + 1
个 不同的 元素nums
中恰有一个元素重复n
次
找出并返回重复了 n
次的那个元素。
思路:
元素出现了n次,所以元素每出现一次就要记录一次次数,而unordered_map就很适合,可以用唯一的key,也就是这个元素对应的值不断++,将所有元素存储到map后,再遍历里面的元素,直到找到对应value等于n次的key值,就是需要求的值。
代码实现:
class Solution {
public:
int repeatedNTimes(vector<int>& nums) {
int n=nums.size();
int k=n/2;
unordered_map<int,int> m;
for(auto &e:nums)
{
m[e]++;
}
for(auto &e:m)
{
if(e.second==k)
{
return e.first;
}
}
return -1;
}
};
2、两个数组的交集I
给定两个数组 nums1
和 nums2
,返回 它们的 交集输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的
思路:
分别将nums1和nums2的元素插入s1,s2两个unordered_set容器去重,再遍历s1容器,同时在s2中查找这个遍历的元素,若存在则将其存入vector数组中返回。
代码实现:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> ret;
unordered_set<int> s1;
for(int &e:nums1)
{
s1.insert(e);
}
unordered_set<int> s2;
for(int &e:nums2)
{
s2.insert(e);
}
for(const int &e: s1)
{
if(s2.find(e)!=s2.end())
{
ret.push_back(e);
}
}
return ret;
}
};
3、两个数组的交集II
给你两个整数数组 nums1
和 nums2
,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[4,9]
思路:
整体思路与上题类似,不过要将交集元素返回在两数组中出现的最小次,所以我们使用unorder_map来存储元素以及出现次数,遍历第一个map,将交集元素往返回数组中存
两个pair中最小的second次。
实现代码:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> ret;
unordered_map<int,int> m1;
for(int &e:nums1)
{
m1[e]++;
}
unordered_map<int,int> m2;
for(int &e:nums2)
{
m2[e]++;
}
for(auto &e:m1)
{
auto tmp=m2.find(e.first);
if(tmp!=m2.end())
{
int n=min(e.second,tmp->second);
while(n--)
{
ret.push_back(e.first);
}
}
}
return ret;
}
};
4、存在重复元素
给你一个整数数组 nums
。如果任一值在数组中出现 至少两次 ,返回 true
;如果数组中每个元素互不相同,返回 false
。
示例 1:
输入:nums = [1,2,3,1] 输出:true
示例 2:
输入:nums = [1,2,3,4] 输出:false
示例 3:
输入:nums = [1,1,1,3,3,4,3,2,4,2] 输出:true
思路:
因为
unordered_set
: 一个集合容器,包含唯一的元素。所以我们利用insert的返回值,每插入一次使用一个变量来记录返回值,判断是否插入成功就可以直到是否存在重复元素:
所有元素都插入成功则没有重复元素
一个插入失败则存在重复元素。
实现代码:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> s1;
for(int &e:nums)
{
auto tmp=s1.insert(e);
if(!tmp.second)
return true;
}
return false;
}
};
5、两句话中不常见的单词
句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。给你两个 句子 s1
和 s2
,返回所有 不常用单词 的列表。返回列表中单词可以按 任意顺序 组织。
示例 1:
输入:s1 = "this apple is sweet", s2 = "this apple is sour" 输出:["sweet","sour"]
示例 2:
输入:s1 = "apple apple", s2 = "banana" 输出:["banana"]
思路:
需要找到在两个句子中出现一次的单词,需要将两个句子中的所有单词存到unordered_map<string,int>中,然后遍历map,将second等于1的单词都存到返回数组中。
就可以得到符合要求的值。
但是需要注意的是,首先需要使用
splitWords
函数将句子分解为单词。
实现代码:
class Solution {
public:
vector<string> splitWords(string s) {
vector<string> words;
string word;
int start = 0;
// 循环遍历字符串s
for (int i = 0; i < s.length(); ++i) {
// 当遇到空格时,将[start, i)范围内的子字符串作为一个单词添加到单词列表中
if (s[i] == ' ') {
word = s.substr(start, i - start);
words.push_back(word);
start = i + 1; // 更新下一个单词的起始位置
}
}
// 处理最后一个单词
word = s.substr(start);
if (!word.empty()) {
words.push_back(word);
}
return words;
}
vector<string> uncommonFromSentences(string s1, string s2) {
vector<string> ret,ss1,ss2;
ss1=splitWords(s1);
ss2=splitWords(s2);
unordered_map<string,int> m;
for(string&e:ss1)
{
m[e]++;
}
for(string&e:ss2)
{
m[e]++;
}
for(auto e:m)
{
if(e.second==1)
{
ret.push_back(e.first);
}
}
return ret;
}
};