leetcode刷题--Hashmap相关

预备知识:

map和hashmap的区别:

hashmap(unordered_map) map
底层为哈希表 底层为红黑树
非按键排序 按键排序
查找效率为O(1) 查找效率为O(logn)
插入的时间是O(logn) 插入的时间是O(logn)
key只能是int、double等基本类型以及string 支持所有类型的键值对

共同点:
键都不能重复
用法类似

例题:

(1)1、两数之和–知道目标值,找下标–简单

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

在这里插入图片描述

#include <vector>
#include <unordered_map>

using namespace std;

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        unordered_map<int, int> hash;  // 哈希表用于记录数字对应的下标

        for (int i = 0; i < nums.size(); ++i) {
    
    
            //寻找hashmap中是否有与当前元素互补的元素
            int n = nums[i];
            int complement = target - n;

            //如果有两个相同的元素之和为target,计算前再插入,无影响
            if (hash.count(complement)) {
    
    
                return {
    
    hash[complement], i};  // 找到答案,返回下标对
            }
            hash[n] = i;  // 记录数字对应的下标
        }

        return {
    
    };  // 找不到答案,返回空数组
    }
};

(2)12整数转罗马数字 --,需要从大到小遍历,所以用的map–中等

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给你一个整数,将其转为罗马数字。

在这里插入图片描述

其它知识点:
map从大到小遍历:
方法(1)重载 () 运算符
①重载 () 运算符,接收两个元素参数,返回一个 bool 类型的比较结果。
②在定义 std::map 或其他关联容器时,将该比较函数对象作为模板参数传入,以覆盖默认的元素比较方式。

#include <iostream>
#include <map>

// 定义一个比较函数对象,按照元素值大小从大到小排序
class GreaterValue {
    
    
public:
    bool operator()(const std::pair<int, int>& p1, const std::pair<int, int>& p2) const {
    
    
        return p1.second > p2.second;
    }
};

int main() {
    
    
    // 定义一个 map,键值类型为 int,值类型为 int
    std::map<int, int, GreaterValue> myMap;
    
    // 插入一些元素
    myMap.insert({
    
    3, 10});
    myMap.insert({
    
    1, 20});
    myMap.insert({
    
    2, 30});

    // 遍历 map,输出每个元素
    for (const auto& elem : myMap) {
    
    
        std::cout << elem.first << " => " << elem.second << '\n';
    }

    return 0;
}

方法(2)利用反迭代器
使用迭代器逆序遍历 map 可以利用反向迭代器 rbegin() 和 rend() 来实现。具体的方法是:对于普通迭代器,我们可以使用前缀自增运算符 ++ 来向前移动迭代器查找 map 元素,而对于反向迭代器,则需要使用前缀自减运算符 – 来向后移动迭代器查找 map 元素

实现中利用了方法(2)

class Solution {
    
    
public:
    string intToRoman(int num) {
    
    
        string result;

        map<int, string> intToRomanStr = {
    
    
            {
    
    1, "I"}, {
    
    4, "IV"}, {
    
    5, "V"}, {
    
    9, "IX"},
            {
    
    10, "X"}, {
    
    40, "XL"}, {
    
    50, "L"}, {
    
    90, "XC"}, 
            {
    
    100, "C"}, {
    
    400, "CD"}, {
    
    500, "D"}, {
    
    900, "CM"}, 
            {
    
    1000, "M"}};
        //逆序遍历map,用反向迭代器
        auto it = intToRomanStr.rbegin();
        //cout<< " it.first = "<< (*it).first << " it.second = "<< (*it).second;
        while(num > 0){
    
    
            //cout<< "num = " << num;
            
            for(; it != intToRomanStr.rend() && (num < (*it).first);it++){
    
    };
            //it指向num第一个大于的数字
            //cout<< " it.first = "<< (*it).first << " it.second = "<< (*it).second;
            int times = num/(*it).first;
            while(times > 0) {
    
    
                result+=(*it).second;
                times--;
            }
            num = num%(*it).first;
            //cout<< " remainder = "<< num << endl;
        }
        return result;

    }
};

(3)575分糖果–简单

Alice 有 n 枚糖,其中第 i 枚糖的类型为 candyType[i] 。Alice 注意到她的体重正在增长,所以前去拜访了一位医生。

医生建议 Alice 要少摄入糖分,只吃掉她所有糖的 n / 2 即可(n 是一个偶数)。Alice 非常喜欢这些糖,她想要在遵循医生建议的情况下,尽可能吃到最多不同种类的糖。

给你一个长度为 n 的整数数组 candyType ,返回: Alice 在仅吃掉 n / 2 枚糖的情况下,可以吃到糖的 最多 种类数。

在这里插入图片描述

其它知识点
判断unordered_map是否存在某键:
判断vector是否存在某键

//判断unordered_map是否存在某键
unordered_map<int, int> tmp;
插入一些值
if(tmp.find(32) == tmp.end()) cout<<"不存在";
else cout<< "存在";

//判断vector是否存在某键
vector<int, int> tmp;
插入一些值
if(find(tmp.begin(), tmp.end(), 32) == tmp.end()) cout<<"不存在";
else cout<< "存在";

(1)使用unordered_map存储糖果种类

class Solution {
    
    
public:
    int distributeCandies(vector<int>& candyType) {
    
    
        unordered_map<int, int> eachTypeNumber;
        int typeNum = 0;
        int maxEatNum = candyType.size()/2;
        //一遍遍历(边遍历边判断),计算糖果的种类及数量
        for(auto it:candyType){
    
    
            int type = it;
            if(eachTypeNumber.find(type) == eachTypeNumber.end()){
    
    
                eachTypeNumber[type] = 0;
                typeNum++;
                if(typeNum >= maxEatNum) return maxEatNum;
            }
            eachTypeNumber[type]++;
        }
        return typeNum;

    }
};

在这里插入图片描述

(2)使用vector存储糖果种类

class Solution {
    
    
public:
    int distributeCandies(vector<int>& candyType) {
    
    
        //存贮种类->数量
        vector<int> eachTypeNumber;
        int typeNum = 0;
        int maxEatNum = candyType.size()/2;
        //一遍遍历(边遍历边判断),计算糖果的种类及数量
        for(auto it:candyType){
    
    
            int type = it;
            //如果hash表中没有添加过该种类,种类数加一
            if(find(eachTypeNumber.begin(), eachTypeNumber.end(), it) == eachTypeNumber.end()){
    
    
                eachTypeNumber.push_back(it);
                typeNum++;
                //如果种类数大于等于可以吃的数量,则返回最大可以吃的数量
                if(typeNum >= maxEatNum) return maxEatNum;
                
            }
        }
        //如果全部遍历完种类数还小于可以吃的数量,就返回种类数
        return typeNum;

    }
};

在这里插入图片描述
unordered_map明显比vector快不少

(4)409最长回文串–简单

给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。

在构造过程中,请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。
在这里插入图片描述

知识点

map数据结构的使用
map的遍历方法
hash函数

代码

class Solution {
    
    
public:
    int longestPalindrome(string s) {
    
    
        map<char, int> node_map;
        //记录每个字符出现的个数
        for(int i = 0; i< s.length(); i++){
    
    
            if( node_map.find(s[i]) == node_map.end() )
                node_map[s[i]] = 0;
            node_map[s[i]]++;
        }
        //迭代器
        map<char, int>::iterator it;
        int sum = 0;
        int mediumFlag = 0;
        for(it = node_map.begin(); it != node_map.end(); it++){
    
    
            //如果该字符出现次数为偶数,则可对称摆放
            if(it->second % 2 == 0) sum += it->second;
            //如果出现次数为奇数,且未有中间字符,则一个字符放中间,其余对称摆放
            else if(mediumFlag == 0) {
    
    
                sum += it->second;
                mediumFlag = 1;
            }
            //如果出现次数为奇数且已有中间字符,则丢弃一个字符,其余对称摆放
            else sum += it->second -1;
        }return sum;

    }
};

结果

在这里插入图片描述

(5)290单词规律–简单

给定一种规律 pattern 和一个字符串 s ,判断 s 是否遵循相同的规律。

这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。

在这里插入图片描述

知识点

hash_map
hash_map 的遍历

代码

class Solution {
    
    
public:
    bool wordPattern(string pattern, string s) {
    
    
        //字符到单词的映射要唯一
        //单词到字符的映射也要唯一
        map<char, string> match;
        map<string, char> match_str_char;
        char patternChar;
        int j = 0;
        for(int i = 0; i<pattern.length(); i++){
    
    
            string temp_str = "";
            //如果pattern还没遍历结束,s已经遍历结束,则未匹配成功
            if(j>= s.length()) return false;
            else{
    
    
                //提取字符串
                while(j< s.length() && s[j] != ' '){
    
    
                    temp_str+= s[j];
                    j++;
                }
                if(s[j] == ' ') j++;
            }
            //如果未在字符到单词的映射中找到该字符
            if(match.find(pattern[i]) == match.end()){
    
    
                //添加映射
                match[pattern[i]] = temp_str;
                //如果在单词到字符的映射中未找到该字符,添加映射
                if(match_str_char.find(temp_str) == match_str_char.end()) match_str_char[temp_str] = pattern[i];
                //否则,若单词已经对应了其它字符,返回假
                else if(match_str_char[temp_str] !=pattern[i]) return false;
            }
            //否则,若字符已经对应了其它单词,返回假
            else if(match[pattern[i]] != temp_str) return false;
            
        }
        //如果字符已经遍历完成,单词还未完成,则返回假
        if(j < s.length()) return false;
        return true;
    }
};

提交结果

在这里插入图片描述

(6)49字母异位词分组–中等

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
在这里插入图片描述

知识点

hash_map
hash_map的遍历
sort函数的使用
字符串排序

代码

class Solution {
    
    
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
    
    
        vector<vector<string>> result;
        if(strs.size() == 0) return result;
        map<string, vector<string>> hash_map;
        for(int i = 0; i< strs.size(); i++){
    
    
            string temp_str = strs[i];
            //字符串的排序,组成字符相同的字符串,排序后的结果都是一样的
            sort(temp_str.begin(), temp_str.end());
            //如果map中没有该字符串,则先添加键
            if(hash_map.find(temp_str) == hash_map.end()) {
    
    
                vector<string> temp;
                hash_map[temp_str] = temp;
            }
            //将字符串push到对应键中
            hash_map[temp_str].push_back(strs[i]);
        }
        map<string, vector<string>>::iterator it;

        //hash_map的遍历,将映射完成的,同一组的值push到result中
        for(it = hash_map.begin(); it!=hash_map.end(); it++){
    
    
            result.push_back(it->second);
        }
        return result;
    }
};

结果

在这里插入图片描述

(7)3无重复字符的最长子串–中等

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
在这里插入图片描述

知识点

双指针维护窗口
hash_map

代码

class Solution {
    
    
public:
    int lengthOfLongestSubstring(string s) {
    
    
        //双指针维护窗口
        int begin = 0;
        int end = 0;
        int result = 0;
        int maxLength = 0;
        map<char, int>char_map;
        //当end未到达最后时,持续循环
        while(end < s.length()){
    
    
            //cout<<"char_map[s[end]]="<<s[end];
            //每向后一个,最长长度加1
            maxLength++;
            //如果最后字符无重复,将hash表中该字符的映射置为1
            if(char_map.find(s[end]) == char_map.end() || char_map[s[end]] == 0) {
    
    
                //cout<<" "<<char_map[s[end]]<<"+1"<< endl;
                char_map[s[end]] = 1;
                
                
            }
            //如果有重复,将begin向后移动,直至无重复为止
            else if(char_map[s[end]] == 1){
    
    
                //cout<<" "<<char_map[s[end]]<<"-1"<<endl;
                while(char_map[s[end]] == 1){
    
    
                    char_map[s[begin]] --;
                    begin++;
                    maxLength--;
                }char_map[s[end]] ++;
            }
            //如果更新后的最长串大于历史最长串,则更新result
            if(maxLength > result) result = maxLength ;
            end++;
        }
        return result;

    }
};

结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44343355/article/details/130847448