哈希表理论基础 :了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。
什么时候想到用哈希法:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。 这句话很重要,在做哈希表题目都要思考这句话。
文章讲解:代码随想录
目录
哈希表-数组
数据量没那么大的时候,用哈希数组比较合适!
242.有效的字母异位词
建议: 这道题目可以感受到数组用来做哈希表 给我们带来的便利之处。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解: 代码随想录
class Solution {
public:
bool isAnagram(string s, string t) {
int hash_array[26] = {0}; //ATTENTION:0数组的初始化方式
for (int i = 0; i < s.size(); i++){
hash_array[s[i] - 'a']++;
}
for(int i = 0; i <t.size(); i++){
hash_array[t[i] - 'a']--;
}
for(int i = 0; i < 26; i++){
if(hash_array[i] != 0){
return false;
}
}
return true;
}
};
改善:
1、前两个for可以写在一起;
2、可以用sort之后比较两个字符串是否相同,49题字母异位词就是这样判断字母异位词的。
383. 赎金信
建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展题
文章讲解:代码随想录
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int hash_arr[26] = {0};
for(int i = 0; i < magazine.size(); i++){
hash_arr[magazine[i] - 'a']++;
}
for(int i = 0; i < ransomNote.size(); i++){
hash_arr[ransomNote[i] - 'a']--;
if(hash_arr[ransomNote[i] - 'a']<0){
return false;
}
}
return true;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(1)
49. 字母异位词分组
题目链接:49. 字母异位词分组 - 力扣(LeetCode)
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> res;
unordered_map<string, vector<string>> map;
for(int i = 0; i < strs.size(); i++){
string str = strs[i];
sort(str.begin(), str.end());
map[str].push_back(strs[i]);
}
for(auto iter: map){
res.push_back(iter.second);
}
return res;
}
};
428.找到字符串中所有的字母异位词
题目链接:438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
字符串:string.substr(开始索引,截取几个)
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> out;
int psize = p.size();
// cout<<psize<<endl;
for(int i = 0; i < s.size(); i++){
// cout<<i<<i+psize<<endl;
string str_temp = s.substr(i,psize);//ATTENTION
// cout<<str_temp<<endl;
if(matchString(str_temp, p) == true){
out.push_back(i);//ATTENTION
}
}
return out;
}
bool matchString(string str1, string str2) {
int array_hash[26] = {0};
for (int i = 0; i < str1.size(); i++){
array_hash[str1[i] - 'a']++;
}
for (int i = 0; i < str2.size(); i++){
array_hash[str2[i] - 'a']--;
}
for (int i = 0; i < 26; i++){
if(array_hash[i]!=0){
return false;
}
}
return true;
}
};
string.substr(初始索引,索引数量)
vector.push_back(数据) 在vector后面添加数据
滑动窗口的做法:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
【补充】C++ STL
C++ STL(“Standard Template Library”的缩写,中文译为“标准模板库”)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、列表、集合、链表、队列、栈。
C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。
【总结】C++ 基础数据结构 —— STL之集合(set)用法详解_c++ stl set-CSDN博客
【总结】C++ 基础数据结构 —— STL之关联容器(map)用法详解_c++ stl map这个数据结构-CSDN博客
【总结】C++ 数据结构 —— STL之栈(stack)用法详解_c++stl栈-CSDN博客
【总结】C++ 数据结构 —— STL之队列(queue) 用法详解_c++ stl 队列-CSDN博客
【总结】C++ 基础数据结构 —— STL之动态数组(vector)用法详解_stl数组-CSDN博客
给vector赋值
在C++中,给一个std::vector赋值有几种方法。以下是一些常用的方法:
使用初始化列表赋值:
std::vector<int> v = {1, 2, 3, 4, 5};
使用std::vector的赋值操作符:
std::vector<int> v;
v.assign({1, 2, 3, 4, 5}); // 使用初始化列表
std::vector<int> v2;
v2.assign(5, 10); // 使用指定的值填充容器
std::vector<int> v3;
v3.assign(v.begin(), v.end()); // 使用另一个vector的迭代器范围
使用std::fill算法:
std::vector<int> v(5);
std::fill(v.begin(), v.end(), 10); // 使用std::fill填充容器
使用std::generate算法:
std::vector<int> v(5);
std::generate(v.begin(), v.end(), []() { return rand() % 10; }); // 使用lambda函数生成值
使用std::vector的swap方法与另一个vector交换内容:
std::vector<int> v;
std::vector<int> v2 = {1, 2, 3, 4, 5};
v.swap(v2); // 交换v和v2的内容
使用std::copy算法从另一个容器复制内容:
std::vector<int> v(5);
std::vector<int> v2 = {1, 2, 3, 4, 5};
std::copy(v2.begin(), v2.end(), v.begin()); // 从v2到v
这些是给std::vector赋值的一些常用方法。选择哪种方法取决于具体的需求和场景。
哈希表-set
349. 两个数组的交集
建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议忽略这个条件。 尝试去使用set。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
unordered_set 不需要去重(与mult_set不同)
unordered_set的定义: unordered_set<int> set2;
unordered_set的定义+初始化:unordered_set<int> set1(nums1.begin(),nums1.end());
set.find(元素):从set容器中查找值为x的元素O(log n):若存在,返回一个迭代器,指向键值x;若不存在,返回一个迭代器,指向set.end()。
set.insert(元素):向集合中插入元素
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set1(nums1.begin(),nums1.end());//ATTENTION
unordered_set<int> set2;
for (int num:nums2){//ATTENTION
if(set1.find(num) != set1.end()){//找到了//ATTENTION
set2.insert(num);
}
}
return vector<int> (set2.begin(),set2.end());//ATTENTION
}
};
202. 快乐数
建议:这道题目也是set的应用,其实和上一题差不多,就是套在快乐数一个壳子
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解:代码随想录
难点:1、说到sum可以无限循环意味着sum如果重复出现会导致无线循环,因此可以用set
来存放,看看能不能找到重复的;2、数字的单位操作
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> set;
while(1){
int sum = getSum(n);
if(sum == 1){
return true;
}
if(set.find(sum)!=set.end()){
return false;
}else{
set.insert(sum);
}
n = sum;
}
}
int getSum(int n){
int sum = 0;
while(n){
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
};
3. 无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> set;
int left =0;
int maxStr = 0;
for(int i = 0; i < s.size(); i++){
while(set.find(s[i]) != set.end()){//如果找到重复的了
set.erase(s[left]);//从左边开始擦,擦到不包括本s[i],后面再把它插进来
left ++;//left的作用就是把从左边开始擦
}
//maxStr = max(maxStr,i-left+1);//或者这样也可以
set.insert(s[i]);
maxStr = maxStr > set.size() ? maxStr : set.size();
}
return maxStr;
}
};
哈希表 - map
map : key value
map 的定义:unordered_map<int,int> map1;
map.find(元素):返回指向该元素的迭代器
插入:
直接map[键]=键值
map.insert(e):插入一个value_type类型的值
map.insert(beg, end):beg和end标记的是迭代器的开始和结束
map.insert(iter, e)
1. 两数之和【语法,回顾】
建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。 先看视频讲解,然后尝试自己写代码,在看文章讲解,加深印象。
文章讲解/视频讲解:代码随想录
如果尝试用hash-set做的话,是不行的,因为不能获取到匹配的索引
//errrrrrrrrror
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_set<int> set1;
vector<int> res;
for(int i = 0; i < nums.size(); i++){
if(set1.find(target-nums[i])!=set1.end()){
res.push_back(nums[i]);
// set1.find(target-nums[i]);
res.push_back(...);//希望获取索引,但是因为是集合所以不行
return res;
}else{
set1.insert(nums[i]);
}
}
return res;
}
};
如果是map就可以
class Solution {public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> map1;
for(int i = 0; i < nums.size(); i++){
if(map1.find(target-nums[i])!=map1.end()){
return {map1.find(target-nums[i])->second,i};
}else{
map1.insert(pair<int,int>(nums[i],i));//内容,下标
//或者:map1[nums[i]]=i;
}
}
return {};
}
};
454.四数相加II
建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> map_12;//sumvalue & nums
int count = 0;
for(int i:nums1){
for(int j:nums2){
map_12[i+j]++;//ATTENTION
}
}
// DONT DO THAT
// for(int i = 0; i<nums1.size(); i++){
// for(int j = 0; j<nums2.size(); j++){
// count12++;
// map_12.insert(pair<int><int(nums1[i]+nums[j],count12)>); //不好统计count12
// }
// }
for(int p:nums3){
for(int k:nums4){
// map_34[p+k]++;
if(map_12.find(0-(p+k))!=map_12.end()){
count = count + map_12[0-(p+k)];
}
}
}
return count;
}
};
15. 三数之和 【难,双指针,去重】
建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。
题目链接:
文章讲解/视频讲解:代码随想录
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for(int i = 0 ; i < nums.size(); i++){
if(nums[i] > 0) return res;//res中已经存了好几组满足题意的了,可以返回了
//去重i
if(i > 0 && nums[i - 1] == nums[i]) continue;
int left = i + 1;
int right = nums.size() - 1;
while(left < right){//因为不能重叠所以<
if(nums[i] + nums[left] + nums[right] > 0) right--;
else if(nums[i] + nums[left] + nums[right] < 0) left++;
else{//找到满足条件的了
res.push_back(vector<int>{nums[i] , nums[left] , nums[right]});
//去重left、right
while(right > left && nums[right] == nums[right - 1]) right--;
while(right > left && nums[left] == nums[left + 1]) left++;
//找到答案后,收缩
right--;
left++;
}
}
}
return res;
}
};
18. 四数之和
建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
文章讲解/视频讲解:代码随想录
如果题目没有要求时间复杂度
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
sort(nums.begin(), nums.end());
int legth = 1;
int res = 1;
if(nums.size() < 2) return nums.size();
for(int i = 1; i < nums.size(); i++){
if(nums[i] - nums[i-1] == 1){
legth ++;
res = res > legth ? res : legth;
}else if(nums[i] == nums[i-1]){}
else{
legth = 1;
}
}
return res;
}
};
但是题目要去时间复杂度是O(n),需要用到哈希表
满射问题/判断是否具有相同规律【hush表】
205. 同构字符串
class Solution {
public:
bool isIsomorphic(string s, string t) {
if(s.size() != t.size()){
return false;
}
unordered_map<char,char> maps2t;
unordered_map<char,char> mapt2s;
int n = s.size();
for(int i = 0; i < n; i ++){
if(maps2t.find(s[i]) == maps2t.end()){
maps2t[s[i]] = t[i];
}
if(mapt2s.find(t[i]) == mapt2s.end()){
mapt2s[t[i]] = s[i];
}
if(maps2t.find(s[i]) != maps2t.end() && maps2t[s[i]] != t[i] ||
mapt2s.find(t[i]) == mapt2s.end() && mapt2s[t[i]] != s[i]){
return false;
}
}
return true;
}
};
290. 单词规律
class Solution {
public:
bool wordPattern(string pattern, string s) {
unordered_map<char,string> maps2t;
unordered_map<string,char> mapt2s;
vector<string> vec = st2vec(s);
if(pattern.size() != vec.size()) return false;
int n = vec.size();
for(int i = 0; i < n; i ++){
if(maps2t.find(pattern[i]) == maps2t.end()){
maps2t[pattern[i]] = vec[i];
}
if(mapt2s.find(vec[i]) == mapt2s.end()){
mapt2s[vec[i]] = pattern[i];
}
if(maps2t.find(pattern[i]) != maps2t.end() && maps2t[pattern[i]] != vec[i] ||
mapt2s.find(vec[i]) != mapt2s.end() && mapt2s[vec[i]] != pattern[i]){
return false;
}
}
return true;
}
private:
vector<string> st2vec(string str){
vector<string> res;
string word = "";
for(char i : str){
if(i == ' '){
res.push_back(word);
word= "";
}else{
word += i;
}
}
res.push_back(word);
return res;
}
};
219. 存在重复元素 II
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int, vector<int>> map;
for(int i = 0; i < nums.size(); i++){
map[nums[i]].push_back(i);
}
for(auto p : map){
vector<int> vec = p.second;
if(vec.size() > 1){
return judge(vec, k);
}
}
return false;
}
bool judge(vector<int>& vec, int k){
for(int i = 1; i < vec.size(); i++){
if(vec[i] - vec[i - 1] <= k) return true;
}
return false;
}
};