力扣 哈希表 总结

472 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1. 杂项

哈希表的讲解,很细很细

定义一个哈希表

	unordered_map<typenameA,typenameB> hash; // key  value

常用函数

	//增加元素
	hash[key] = value;
	//删除元素
	hash.erase(key);  //  如果没有找到会返回0
	//修改元素
	hash[key] = new value;
	//查找元素
	hash.find(key);   //  常用hash.find(key) == hash.end()来判断key值是否存在
	hash.count(key)   //  该函数:用以统计key值在unordered_map中出现的次数。c++ unordered_map不允许有重复的key。因此,如果key存在,则count返回1,如果不存在,则count返回0

	//————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
	//遍历元素
    for (auto it = mp.begin(); it != mp.end(); ++it)  //遍历的方法
    {
        cout <<"key为:" << it->first << "   value为:";
        for (int i = 0; i < it->second.size(); i++)  
        { 
            cout << (it->second)[i]<<' ';  //这边的value是vector<string>类型的,不能直接用cout打印
                                            //,所以要循环一个一个字符串打印出来
        }
        cout << endl;        
    }

2. emplace_back 函数解析

也是添加元素的函数,只不过和vector容器的push_back()的区别在于: push_back()方法要调用构造函数和复制构造函数,这也就代表着要先构造一个临时对象,然后把临时的copy构造函数拷贝或者移动到容器最后面。而emplace_back()在实现时,则是直接在容器的尾部创建这个元素,省去了拷贝或移动元素的过程。

vector<pair<int, int>> ret;
ret.push_back(1,1)//会报错,因为没有构造一个临时对象
ret.push_back(pair(1,1))//不会报错,因为构成了一个pair对象
ret.emplace_back(1,1)//不会报错,因为直接在容器的尾部创建对象
vector<vector<int>> ans;
        int lastcol = INT_MIN;
        for (const auto& [col, row, value]: nodes) {
            if (col != lastcol) {
                lastcol = col;
                ans.emplace_back();//这里的emplace_back()直接在ans的尾部创建一个类型为vector<int>的空对象,如果省去这一行,后面的ans.back()会是一个空指针而报错。
            }
            ans.back().emplace_back(value);
        }

3.哈希表的应用总结

3.1 力扣49 (字母异位词)

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 ==字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。== 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

这边的键值对采用的是:由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。

    vector<string> strs;
    unordered_map<string, vector<string>> mp;
    strs.push_back("eat");
    strs.push_back("tea");
    strs.push_back("tan");
    strs.push_back("ate");
    strs.push_back("nat");
    strs.push_back("bat");
        
    for (string& str : strs) { //确定好排序好的key!
        string key = str;
        sort(key.begin(), key.end());
        mp[key].emplace_back(str);  //这边插入的key和value相对应!
    }

    for (auto it = mp.begin(); it != mp.end(); ++it)  //遍历的方法
    {
        cout <<"key为:" << it->first << "   value为:";
        for (int i = 0; i < it->second.size(); i++)  
        { 
            cout << (it->second)[i]<<' ';  //这边的value是vector<string>类型的,不能直接用cout打印
                                            //,所以要循环一个一个字符串打印出来
        }
        cout << endl;        
    }
    //排好序的结果如下图,最后按照下面的这个图 按照顺序放到 ans 里面就行了
    vector<vector<string>> ans;
    for (auto it = mp.begin(); it != mp.end(); ++it) {
        ans.emplace_back(it->second);
    }

image.png

image.png

3.2 力扣49 法二 另一种键值对方式

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 ==字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。== 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

由于互为字母异位词的两个字符串包含的字母相同,因此两个字符串中的相同字母出现的次数一定是相同的,故可以将每个字母出现的次数使用字符串表示,作为哈希表的键。 由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为 2626 的数组记录每个字母出现的次数。需要注意的是,在使用数组作为哈希表的键时,不同语言的支持程度不同,因此不同语言的实现方式也不同。

    vector<string> strs;
    strs.push_back("eat");
    strs.push_back("tea");
    strs.push_back("tan");
    strs.push_back("ate");
    strs.push_back("nat");
    strs.push_back("bat");
        
    vector<vector<string>> res;
    map<string, vector<string>> map;

    // 统计string的各字母频次,以频次为key直接加入队列
    for (auto s : strs) {
        string st = string(26, '0');  //先是弄成26个字母都是零
        for (auto c : s)  ++st[c - 'a']; //将有出现的字母对应的位置加 1
        map[st].emplace_back(s);  //放到键值对当中, st作为key是这个26个数   s作为value是正常的字符串
    }
    for (auto e : map)  res.emplace_back(e.second);  //放到 vector 当中

	//遍历方法
    for (auto it = map.begin(); it != map.end(); ++it)  //遍历的方法
    {
        cout << "key为:" << it->first << "   value为:";
        for (int i = 0; i < it->second.size(); i++)
        {
            cout << (it->second)[i] << ' ';  //这边的value是vector<string>类型的,不能直接用cout打印
                                            //,所以要循环一个一个字符串打印出来
        }
        cout << endl;
    }

image.png

3.3 力扣451 哈希表的深入理解

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。 返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

输入: s = "tree" 输出: "eert" 解释: 'e'出现两次,'r'和't'都只出现一次。 因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。 ==直接先按照字母出现的频率来创建哈希表,这样key为字母值,value为他出现的频率。 然后将其放到vector当中按照value的大小进行从大到小排序。 最后放到string reuslt当中即可。==

	string s;
    s = "baafAAag";
    unordered_map<char, int> mp; //哈希表这样创建的,key是char类型,value是int类型
    int length = s.length();
    for (auto& ch : s) {
        mp[ch]++;  //这里怎么放key和value的!!很巧妙
    }
    //遍历哈希表元素
    for (auto it = mp.begin(); it != mp.end(); ++it)  //遍历的方法
    {
        cout << "key为:" << it->first << "   value为:";
        cout << it->second;
        cout << endl;
    }

image.png

    vector <pair<char, int>> temp;  //创建内容格式和哈希表一致的vector容器,将哈希表的内容放到vector容器当中
    for (auto& it : mp) {
        temp.push_back(it); //格式一样,直接push_back
    }
    
    for (int i = 0; i < temp.size(); i++) {
        cout << temp[i].first << "  ";
        cout << temp[i].second << endl;
    }

image.png

    for (int i = 0; i < temp.size() - 1; ++i) {   //利用冒泡排序根据value的大小进行排序
        for (int j = 0; j < temp.size() - i - 1; ++j) {
            if (temp[j].second < temp[j+1].second)
            {
                pair<char, int> tempp;
                tempp = temp[j];
                temp[j ] = temp[j+1];
                temp[j+1] = tempp;
            }
        }
    }
    
    for (int i = 0; i < temp.size(); i++) {
    cout << temp[i].first << "  ";
    cout << temp[i].second << endl;
    }

image.png

    string result;  
    for (auto& it : temp) {
        result = result.append(it.second, it.first);  //最后按照顺序放到string reuslt当中即可
    }
    cout << result << endl;

image.png