哈希法c++

一、哈希表基础

哈希表(Hash Table)也称散列表,是根据关键码值(key, value)而直接进行访问的数据结构。

如何将键值映射到哈希表上? => 使用哈希函数

        1、将键值映射为哈希表上具体的索引数字:利用hashCode将键值转化为具体数值(hashcode通过特定编码方式,可以将其他数据格式转化为不同的数值)

        2、若hashCode得到的数值大于哈希表的大小:将得到的索引数字对哈希表的大小取模,保证键值一定可以映射到哈希表上。

        3、若有不同的键值映射到了同一个索引数值,则产生了哈希冲突

其中简化代码如下:

//哈希函数:hash_function
index_value=hash_function(key); //key是键值
int hash_function(int key,int hash_size){
    return hash_code(key)%hash_size; //hash_size代表哈希表大小
}

解决哈希冲突的方法

主要有两种方法:开放地址法和链地址法(也叫拉链法)

        1、开放地址法

        用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测算法在散列表中寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。按照探测序列的方法,一般将开放地址法区分为线性探查法、二次探查法、双重散列法等。(要求hash_size一定要大于data_size,要不然哈希表上就没有空置的位置来存放冲突的数据了)

        2、拉链法

        用拉链法解决冲突的做法是:当冲突发生时,将发生冲突的元素(键值)存储在链表中,可以通过索引找到发生冲突的元素。需要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

c++中哈希表的实现 

        在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:

 上表来源于代码随想录

二、unordered_map基本操作

int main()
{
    //哈希表的初始化及插入
    unordered_map<int, string> hash_map={{ 1, "熊大" },{ 2, "熊二" }};//使用{}赋值
    hash_map[3] = "光头强";  //使用[ ]进行单个插入,若已存在键值3,则赋值修改,若无则插入。
    hash_map.insert(pair<int, string>(4, "拖拉机"));//使用insert和pair插入
    hash_map.emplace(pair<int, string>(5, "树"));//效率比 insert() 方法高

    //常见操作
    if(hash_map.find(2)!=hash_map.end()){
        cout<<"找到键值为2的值为"<<hash_map.at(2)<<endl;
    }
    hash_map.erase(2);//删除键值为2的成员
    cout << "hash_map size==" << hash_map.size() << endl;//输出hash_map大小

	//遍历输出
    for(auto it=hash_map.begin();it!=hash_map.end();it++){
        cout<<it->first<<" "<<it->second<<endl;
    }

    getchar();  
    return 0;
}

三、哈希表常见力扣题

3. 无重复字符的最长子串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char>res;
        int left=0,right=0;
        int length=0;
        while(right<s.size()){
            while(res.find(s[right])!=res.end()){//保证滑动窗口内没有重复字母
                res.erase(s[left]);
                left++;
            }    
            res.insert(s[right]);
            length=max(length,right-left+1);
            right++;
        }
        return length;
    }
};

17. 电话号码的字母组合

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        vector<string> combinations;
        if(digits.empty()){
            return combinations;
        }  
        unordered_map<char,string> phonemap={
            {'2',"abc"},
            {'3',"def"},
            {'4',"ghi"},
            {'5',"jkl"},
            {'6',"mno"},
            {'7',"pqrs"},
            {'8',"tuv"},
            {'9',"wxyz"}
        };
        string combination;
        backtrace(phonemap,combinations,digits,0,combination);
        return combinations;
    }
    void backtrace(const unordered_map<char,string>& phonemap,vector<string>& combinations,string& digits,int index,string& combination){
        if(index==digits.length())
            combinations.push_back(combination);
        else{
            char digit=digits[index];
            const string& letters=phonemap.at(digit);
            for(const char& letter:letters){
                combination.push_back(letter);
                backtrace(phonemap,combinations,digits,index+1,combination);
                combination.pop_back();
            }
        }
    }
};

49. 字母异位词分组

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> hashmap;
        for(string& str:strs){
            string key=str;
            sort(key.begin(),key.end());
            hashmap[key].emplace_back(str);
        }
        vector<vector<string>> res;
        for(auto it=hashmap.begin();it!=hashmap.end();it++){
            res.emplace_back(it->second);
        }
        return res;
    }
};

76. 最小覆盖子串

class Solution {
public:
    unordered_map <char, int> ori, cnt;
    bool check(){
        for(const auto &it:ori){
            if(cnt[it.first]<it.second)
                return false;
        }
        return true;
    }
    string minWindow(string s, string t) {
        if(t.size()>s.size()) return {};
        int left=0,right=-1; //注意right初始化为-1
        int left_out=-1;
        int length=INT_MAX;
        
        string out={};
        for(const auto &it:t){
            ori[it]++;
        }
        while(right<int(s.size())){ //注意int类型转换,因为s.size()类型为unsigned int,right初始化为负数
            right++;
            if(ori.find(s[right])!=ori.end()){
                cnt[s[right]]++;
            }
            while(left<=right&&check()){
                if((right-left+1)<length){
                    length=right-left+1; //记录最小串长度
                    left_out=left; //记录最小串起始位置
                }
                if(ori.find(s[left])!=ori.end()){
                    cnt[s[left]]--;
                }
                left++;
            }
        }
        //cout<<s[right];
        return left_out==-1?out:s.substr(left_out,length);
    }
};

​​​​​​128. 最长连续序列

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> hash;
        for(auto x:nums) hash.insert(x);
        int res=0;
        for(auto x:hash){
            if(!hash.count(x-1)){//找到最小的那个
                int y=x;
                while(hash.count(y+1))y++; //找最大的那个
                res=max(res,y-x+1);
            }
        }
        return res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值