map和set

目录

一、set

1、set的相关函数:

 2、set的使用

①、insert

②、erase

③、find 

④、count

3、关于multiset

①、insert区别

②、count的区别

③、find的区别

④、erase的区别

二、map

1、map的相关函数

2、map的使用

①、insert

 ②、find

③、[]

④、at

3、关于multimap

三、相关OJ题 

1、前k个高频单词

2、求两个数组的交集

3、单词识别


一、set

1、set的相关函数:

下面会挑选常用的进行用法示例

 2、set的使用

下面示范常用的几个函数

①、insert

首先插入几个值,用迭代器和范围for遍历结果打印

 

set的结果有两个作用分别是排序和去重:

我们插入了5个数,其中有两个相同的,但是结果只有4个,说明有去重的作用

观察打印结果是升序的,原因是默认的仿函数是less<int>,下图可见

默认排序为升序,是因为set的底层是搜索二叉树,而搜索二叉树的中序遍历就是升序的

如果想改为降序,只需在set<int>后加greater<int>即可,即set<int,greater<int>>(记得包头文件function)

这时范围for语法,也可以进行遍历 

如果支持迭代器,那就支持范围for,因为范围for的底层就是迭代器实现的

set并不支持改的操作

②、erase

为了方便观察,下面都用范围for遍历,删去了迭代器遍历的代码 

 

直接删除指定的值

③、find 

find可以与erase结合使用。是由于下面的第一个重载,上面直接使用erase直接删除指定的值是第二个重载

例如我们想删除3,但是是传入3的位置进行删除的

注意需要判断pos是否与s.end()相等,不相等才能删除,因为若find没有找到输入的值,则返回s.end(),这时不判断是否等于s.end()直接删除有可能会出错

④、count

count可以检测该值在不在set中,存在返回1,否则返回0

 set中的count更多的还是为了和multiset保持接口的一致性

3、关于multiset

multiset与set的用法几乎一致,但是也有一些差别

multiset允许冗余,上面说到set的作用有去重,即插入两个相同的数时只会插入一个,而multiset允许插入两个相同的数

下面是两个插入时的区别:

①、insert区别

set:

multiset:

所以multiset作用就是单纯的排序,没有了去重的作用

②、count的区别

set是返回值只有1和0,而multiset会返回个数,如下:

插入的数有几个就返回几 

③、find的区别

如果find查找出现多次的相同数值,那么返回排序后的第一个数值的位置 

④、erase的区别

如果删除的是出现多次的数,那么会全部删除


二、map

1、map的相关函数

map和set的功能比较相似,有下面的一些函数

2、map的使用

map中是pair<key,value>

①、insert

这里的insert插入的类型是value_type&,而value_type是pair结构体,如下图:

 而pair结构体中,里面有两个成员,一个是first,一个是second, 

所以map中insert插入的是一个结构体,而不是像set一样插入的是一个数

并且insert的返回值也是pair,这里是为下面的[]做准备的,因为[]的底层就是insert实现的,下面也会提到

下面就是map的insert的常规使用方法:调用构造插入

 打印结果也是根据第一个string内容的ASCII码排序的:

 第二种插入方法:make_pair

make_pair是函数模板,可以自动推出类型

 当然也可以用范围for遍历,加引用是担心拷贝代价太大

 ②、find

find这里的使用,统计出现的个数的样例,这里涉及的知识在二叉搜索树的key/value中也讲到过

第一种统计出现的个数的方法:

用于查找出现的次数,map的find和set的find类似,若没有找到则返回map的end(),所以使用前要先判断返回值是否等于end()

③、[]

[]是在map里找key(key和value就对应pair中的first和second)

若map中有这个key,则返回value的引用,这里就可以有查找和修改value的作用 

group中存在car,那么第一个group["car"]就是查找作用,有则返回car对应的value引用

第二个 group["car"] = "小汽车";就是修改的作用,因为返回的是value的引用,所以可以修改car的value值

如果map中没有这个key,那么就插入pair(key,V()),即调用默认构造,并返回value的引用,这里有插入和修改的作用

group["car"];是插入作用的体现

group["car"] = "小汽车";是查找加修改的作用体现

group["bus"] = "公交车";是插入加修改作用的体现

下面是第二种利用[]进行统计出现个数的方法:

其中的Count[cur]就是在Count中找与cur相等的key,若存在则++该key对应的int值,若没有则插入pair(cur,int()),调用默认构造,int默认为0,++int变为1

 []的底层其实就是使用的insert:

  insert的返回值描述:

意思就是若key已经在map中,返回pair(key_iterator,false) ,即已经存在结点的迭代器,bool为false

若key不在map中,返回pair(newkey_iterator,false) ,即新出现的key的迭代器,bool为true

下面是[]的底层的代码

中间的.first是insert的返回值pair的key_iterator或newkey_iterator,即该key值所在的结点的迭代器,接着解引用后.second是迭代器中的pair的second,即为所对应的value,满足了[]的返回值是value的引用

这里用到了两个pair,一个是insert的pair<iterator,bool>,一个是迭代器中的pair<key,value>

上面图片的代码也可以写成:

pair<iterator,bool> ret = insert(make_pair(key,V())) ;

return ret.first->second;

④、at

at比[]功能少很多

map中存在key,则返回value引用

map中不存在key,则抛异常处理

3、关于multimap

multimap与map大致功能都差不多,但是没有[],因为multimap既然允许冗余,那么统计次数时,若插入多个相同的string值如:铅笔,无法确定到底返回哪个铅笔所对应的value值,所以multimap就不能解决统计次数的问题了

所以也有用multimap的场景,比如说一个单词有两个意思,那么插入两次这个单词,key相同,但是对应的value不相同,这个场景就能用multimap,但是使用的场景不多,大部分情况还是用map

其他用法参考set的multiset的用法,相差不大


三、相关OJ题 

下面举两个OJ题,里面有使用set、map和multimap的场景,以便更好的理解

1、前k个高频单词

题目描述:

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序


示例:

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i" 在 "love" 之前。

第一种思路分析:

我们可以想到先用map的[]统计每个单词出现的次数,由于返回的答案是按单词出现频率由高到低排序,所以可以将map的数据全部插入到优先级队列中,因为优先级队列可以默认排升序,并且我们可以自己写仿函数控制排序规则,然后挨个取出所存数据的first(string类型的单词,要求返回单词)进vector,然后返回vector即可


第一种思路代码实现:

class Solution {
public:
    //使用优先级队列需要自己写仿函数,解决相同频率下按字典顺序排序的问题
    struct Less
    {
        bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const
        {
            if(kv1.second < kv2.second)
                return true;
            //由于是按字典顺序排,所以是kv1.first > kv2.first,不是小于
            if(kv1.second == kv2.second && kv1.first > kv2.first)
                return true;
            return false;
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string ,int> count;
        //使用map来统计次数
        for(auto& e : words)
        {
            count[e]++;
        }

        vector<string> v;
        //初始化优先级队列pq,数据排升序(大堆)
        priority_queue<pair<string, int>, vector<pair<string, int>>, Less> pq;
        for(auto& e : count)
        {
            pq.push(e);
        }
        //前k个单词循环k次,依次取出数据
        while(k--)
        {
            v.push_back(pq.top().first);
            pq.pop();
        }
        return v;
    }
};

第二种思路分析:

首先先用map的[]统计每个单词出现的次数,我们知道map中的数据是默认按key值进行排序的,那接下来我们再用sort算法按map中value值进行排序,并且sort算法是按照自己实现的一个降序且频率相同时按字典顺序排序的Greater的仿函数,这个仿函数需要注意的是,map中数据是按照string排列的,在使用sort排序后,在相同频率时,应该是string小的算大,因为按字典顺序排序需要先取小的,所以要注意符号问题

并且sort算法必须是连续的存储空间的容器才能使用,map并不是连续的存储空间,而vector是连续的存储空间,所以先将数据全部转到vector中,再使用sort算法


第二种思路代码实现:

class Solution {
public:
    //自己模拟实现仿函数,sort函数的排序中使用
    struct Greater
    {
        bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const
        {
            if(kv1.second > kv2.second)
                return true;
            //由于是按字典顺序排,因此将first小的先输出,所以是小于
            if(kv1.second == kv2.second && kv1.first < kv2.first)
                return true;
            return false;
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string ,int> count;
        //使用map来统计次数
        for(auto& e : words)
        {
            count[e]++;
        }
        //使用vector存map的数据,为了使用sort
        //因为vector有连续的存储空间
        vector<pair<string, int>> vp;
        for(auto& e : count)
        {
            vp.push_back(e);
        }
        //使用sort排序,使用了自己实现的Greater仿函数
        sort(vp.begin(),vp.end(),Greater());
        //将排好序的vp中数据的first即单词传入v中
        vector<string> v;
        for(size_t i = 0;i < k; ++i)
        {
            v.push_back(vp[i].first);
        }
        return v;
    }
};

第三种思路分析:

第三种思路就是,我们不用sort排序了,我们用multimap<int,string,greater<int>>排降序,这里将string和int更换位置,因为默认是按照key排序的,不用map是因为map默认去重,如果有相同次数直接去重了,不满足要求


第三种思路代码实现:

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string ,int> count;
        //使用map来统计次数
        for(auto& e : words)
        {
            count[e]++;
        }
        //使用multimap,将int和string换位置
        //并传入greater排key(即出现次数int)的降序
        multimap<int, string, greater<int>> m;
        //将count的数据翻转int和string插入m中
        for(auto& e : count)
        {
            m.insert(make_pair(e.second, e.first));
        }
        vector<string> v;
        //使用迭代器传入multimap的前k个数据的second即单词
        multimap<int, string>::iterator it = m.begin();
        for(size_t i = 0;i < k; ++i)
        {
            v.push_back(it->second);
            ++it;
        }
        return v;
    }
};

2、求两个数组的交集

题目描述:

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。


示例:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

思路分析:

首先用set将两个数组去重加排序,接着用双指针的方法,挨个判断是否相等,小的那个++,等于时就是交集,于是尾插到vector中,直到其中一个指针指向数组的末尾就结束


代码实现:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //用set将两个数组去重加排序
        set<int> s1(nums1.begin(),nums1.end());
        set<int> s2(nums2.begin(),nums2.end());

        vector<int> v;
        //给两个指针
        auto it1 = s1.begin();
        auto it2 = s2.begin();
        //小的++,等于就说明是交集
        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 < *it2)
            {
                it1++;
            }
            else if(*it2 < *it1)
            {
                it2++;
            }
            else
            {
                v.push_back(*it1);
                it1++;
                it2++;
            }
        }
        return v;
    }
};

3、单词识别

题目描述:

输入一个英文句子,把句子中的单词(不区分大小写)按出现次数按从多到少把单词和次数在屏幕上输出来,次数一样的按照单词小写的字典序排序输出,要求能识别英文单词和句号。


示例:

输入:A blockhouse is a small castle that has four openings through which to shoot.

输出:

a:2
blockhouse:1
castle:1
four:1
has:1
is:1
openings:1
shoot:1
small:1
that:1
through:1
to:1
which:1


思路分析:

首先,应该注意英文句子有空格,所以应该用getline输入,其次不区分大小写,那就遍历一遍统一将大写化为小写

接着再遍历这个string字符串,遍历过程中将其中的单词取出来插入到vector<string>中

然后利用map<string,int>统计单词出现次数,然后就和上面第一题一样,有三种方法,这里只列举一种:我们用multimap<int,string,greater<int>>排降序,将string和int更换位置插入到multimap中,用multimap而不用map的理由和第一题一样,map会去重,我们的int位于key位置,如果有出现相同次数的单词去重会不符合题意

最后按要求格式一个个打印出来即可


代码实现:

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <functional>
using namespace std;

int main() {
    string s;
    getline(cin, s);
    //将大写换为小写
    for(size_t i = 0; i < s.size(); ++i)
    {
        if (s[i] >= 'A' && s[i] <= 'Z')
            s[i] += 32;
    }
    vector<string> v;
    //临时字符串tmp,用于插入每一个单词
    string tmp;
    for (int i = 0; i < s.size(); ++i)
    {
        //如果是空格或句号结尾,说明刚好经过一个单词
        //尾插入vector后,将tmp置空
        if (s[i] == ' ' || s[i] == '.')
        {
            v.push_back(tmp);
            tmp = "";
        }
        //如果不是空格或句号,说明单词没结束
        else
        {
            tmp += s[i];
        }
    }
    //用map的[]统计单词出现次数
    map<string, int> m;
    for (auto& e : v)
    {
        m[e]++;
    }
    //使用multimap进行降序排序,并交换string,int位置
    //因为multimap都是按照key排序的
    multimap<int, string, greater<int>> mp;
    for (auto& e : m)
    {
        mp.insert(make_pair(e.second, e.first));
    }
    //依次打印出结果
    auto pos = mp.begin();
    while (pos != mp.end())
    {
        cout << pos->second << ":" << pos->first << endl;
        pos++;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值