常见的三种哈希
- 数组 :
- 使用数组来做哈希的题目,是因为题目都限制了数值的大小
- 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费
- set集合 :
- 直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的
- 所以数组做哈希有存在的必要
- map映射
- 使用哈希映射的情况
- 需要判断元素是否出现过
- 需要判断元素是否在集合中出现过
函数的使用
set、multiset和unordered_set是三种不同的关联容器, 不对value做修改, 只删除
set
1.是一个有序的集合, 基于红黑数实现, 不允许存储重复的元素。
2.特点:
- 元素自动排序
- 不允许有重复元素
multiset
1.与set类似,但它允许存储重复的元素
2.特点:
- 元素自动排序
- 允许有重复元素
unordered_set
1.unordered_set是一个基于哈希表实现的无序集合,它也不允许存储重复的元素。
2.特点
- 元素无序
- 不允许有重复元素
- 通常提供更快的插入和删除操作,但查找速度可能不如set
#include<bits/stdc++.h>
using namespace std;
//自定义比较大小
struct cmp {
bool operator()(const int& a, const int& b) const {
return a > b;
}
};
int main()
{
set<int> myset;
// set<int, cmp>myset;
// set<int,less<int>>myset;
//unordered_set<int> myset;
//multiset<int> myset;
//插入元素
myset.insert(30);
myset.insert(20);
myset.insert(10);
myset.insert(10);
// 查找元素
cout << "Is 30 in myset ? " << (myset.find(30) != myset.end()) << endl;
//删除元素
myset.erase(10);
//遍历元素
cout << "myset contains:";
for (auto it = myset.begin(); it != myset.end(); ++it) { //遍历方式1
cout << *it << " "; // 结果: 10 20 30
}
/*for (int x : myset) { //遍历方式2
std::cout << x << " ";
}*/
cout << endl;
}
map、multimap和unordered_map
#include<bits/stdc++.h>
using namespace std;
int main() {
map<int, string> myMap;
//unordered_map<int, string> myMap;
//multimap<int, string> myMap;
//map和unordered_map可用两种方式实现插入
myMap[3] = "three";
myMap[1] = "one";
myMap[2] = "two";
myMap[1] = "ONE"; // 更新键1的值,而不是插入新元素
//multimap只能依靠这种方式插入
//myMap.insert(make_pair(3, "three"));
//myMap.insert(make_pair(2, "two"));
//myMap.insert(make_pair(1, "one"));
//myMap.insert(make_pair(1, "ONE"));//在unordered_map和map相当于指向失败
auto it = myMap.begin();
//myMap.erase(it);//对于multimap只删除了make_pair(1, "ONE")值
myMap.erase(1);//删除所有为1的键值
for (const auto& pair : myMap) {
cout << pair.first << " => " << pair.second << endl;
}
return 0;
}
函数的构造方法
242.有效的字母异位词
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
思路: 哈希映射
//map的方式实现
class Solution {
public:
bool isAnagram(string s, string t) {
unordered_map<char, int> mymap;
for (int i = 0; i < s.length(); i++) {
mymap[s[i]]++;
}
for (int i = 0; i < t.length(); i++) {
mymap[t[i]]--;
if (mymap[t[i]] < 0)
return false;
}
for (auto it = mymap.begin(); it != mymap.end(); it++) {
if (it->second != 0)
return false;
}
return true;
}
};
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int> count(26, 0);
for (int i = 0; i < s.size(); i++)
count[s[i] - 'a']++;
for (int i = 0; i < t.size(); i++)
count[t[i] - 'a']--;
for (int i = 0; i < count.size(); i++) {
if (count[i] != 0) return false;
}
return true;
}
};
349.两个数组的交集
给定两个数组 nums1
和 nums2
,返回 它们的交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
思路:
- 使用unordered_set作为映射, 保存索引过的下标
- 使用set集合对结果的去重
使用set集合做映射的原因
- 题目没有限制数组范围, 不能用确定的数组实现
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> myset(nums1.begin(), nums1.end());
set<int> result;
for (int i = 0; i < nums2.size(); i++) {
auto it = myset.find(nums2[i]);
if (it != myset.end()) {
result.insert(*it);
}
}
return vector<int>(result.begin(), result.end());
}
};
350.快乐数
理论基础:
- 在不断求和的过程中, sum的值会重复循环出现
//方法1: 哈希
class Solution {
public:
int conv(int n) {
int sum = 0;
while (n) {
int tem = n % 10;
sum += (tem * tem);
n = n / 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> myset;
while (1) {
n = conv(n);
if (n == 1)
return true;
auto it = myset.find(n);
if (it == myset.end())
myset.insert(n);
else
return false;
}
}
};
//方法2: 双指针法, 相当于链表找环
class Solution {
public:
int conv(int n) {
int sum = 0;
while (n) {
int tem = n % 10;
sum += (tem * tem);
n = n / 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n, fast = n;
while (1) {
slow = conv(slow);
fast = conv(fast);
fast = conv(fast);
if (slow == fast) break;
}
if (slow == 1) {
return true;
} else {
return false;
}
}
};
1.两数之和
使用map的情况
- 需要判断某个数是否在集合出现过, 并且还需要知道其下标位置
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> mymap;
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
int value = target - nums[i];
auto it = mymap.find(value);
if(it == mymap.end()) {
mymap[nums[i]] = i;
} else {
result.push_back(it->second);
result.push_back(i); //可优化成: return {it->second, i};
break;
}
}
return result; //可优化成: return {};
}
};
相关题目
有效的字母异位词:
49.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词
438.找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
两个数组的交集
350.两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序
题解
1.哈希表
2.排序+双指针