代码随想录第6天|哈希

哈希理论基础

常见的三种哈希

  • 数组 :
    • 使用数组来做哈希的题目,是因为题目都限制了数值的大小
    • 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费
  • 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;
}

函数的构造方法

在这里插入图片描述

vector的构造方法

在这里插入图片描述

set, map的构造方法

在这里插入图片描述

unordered_set, unordered_map的构造方法

242.有效的字母异位词

给定两个字符串 st ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 st 中每个字符出现的次数都相同,则称 st 互为字母异位词。
在这里插入图片描述
思路: 哈希映射

//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.两个数组的交集

给定两个数组 nums1nums2 ,返回 它们的交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
在这里插入图片描述

思路:

  1. 使用unordered_set作为映射, 保存索引过的下标
  2. 使用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 {};
    }
};


相关题目
有效的字母异位词:

383.赎金信(opens new window)

49.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词
在这里插入图片描述

438.找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
在这里插入图片描述


两个数组的交集
350.两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序
在这里插入图片描述
题解
1.哈希表
2.排序+双指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值