set和map的使用讲解与OJ练习

序列式容器:vector、list、deque、forward_list(C++11)等,这些底层使用线性表的数据结构,里面存储的是元素本身。

关联式容器:数据和数据之间有紧密的关联,如set,map,AVL树,哈希等。里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

set使用讲解

set是K模型,map是KV模型。set中的底层使用二叉搜索树(红黑树)来实现,查找某个元素的时间复杂度为: l o g 2 N log_2 N log2N

//set
template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare 仿函数
           class Alloc = allocator<T>      // set::allocator_type 空间配置器
           > class set;

//multiset
template < class T,                        // multiset::key_type/value_type
           class Compare = less<T>,        // multiset::key_compare/value_compare
           class Alloc = allocator<T> >    // multiset::allocator_type
           > class multiset;

序列式容器的插入大多为push/pop,而关联式容器用的是insert/erase。

set的本质是排序+去重multiset是单纯的排序。走迭代器输出,是按照中序来输出的。

setmultiset的拷贝构造,底层肯定涉及深拷贝。算法库的find是暴力查找 O ( N ) O(N) O(N),从头找到尾。setfind是按二叉搜索树的原理来进行查找 O ( l o g N ) O(logN) O(logN)multisetfind是找该元素在中序的第一个元素。

set不允许数据冗余,multiset允许有数据冗余。count函数对于multiset更有意义。

lower_bound会返回大于等于该值>=val的迭代器位置【即返回中序里val或val的下一个元素】,upper_bound返回大于该值>val的迭代器位置【即返回中序里val或val的下一个元素】,方便我们获得左闭右开的区间。两者的区别是如果set包含等于 val 的元素:在这种情况下,lower_bound返回指向该元素的迭代器,而upper_bound返回指向下一个元素的迭代器。

当用erase删除迭代器区间void erase (iterator first, iterator last)进行删除时,删除的区间是[first,last)

set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

扫描二维码关注公众号,回复: 14747072 查看本文章

map使用讲解

set是K模型,map是KV模型。map的底层为平衡搜索树(红黑树),查找某个元素的时间复杂度为 O ( l o g 2 N ) O(log_2 N) O(log2N)

map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。operator[]中实际进行插入查找。

//map
template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;
           
//multimap
template < class Key,                                     // multimap::key_type
           class T,                                       // multimap::mapped_type
           class Compare = less<Key>,                     // multimap::key_compare
           class Alloc = allocator<pair<const Key,T> >    // multimap::allocator_type
           > class multimap;

键值对pair

在map中,键值key通常用于排序和唯一的标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair

看一下map中出现的pair键值对,pair用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息,一般把第一个参数当作key,第二个参数当作value

typedef pair<const Key, T> value_type;
------------------SGI版本中关于pair的定义------------------
template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair(): first(T1()), second(T2())	{}
	pair(const T1& a, const T2& b): first(a), second(b)	{}
};
//在写insert的时候,mymap.insert(pair<string, string>("排序", "sort")); 可以使用make_pair来代替 
//mymap.insert(make_pair("中间", "middle"));
------------------------------------------
template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
	return ( pair<T1,T2>(x,y) );
}

operator[]

查找(体现在bool)+插入(体现在insert)+修改(体现在V&)

V& operator[](const K& k)
{
    pair<iterator, bool> ret = insert(make_pair(k, V()));
    //return *(ret.first).second;
    return ret.first->second;
}

调用该函数等价于(*((this->insert(make_pair(k,mapped_type()))).first)).second,可以看出来,底层实际上调用了insert,pair<iterator,bool> insert (const value_type& val);,second是bool,即true表示key为新插入的,false表示该元素已存在。

相当于(this->insert(make_pair(k,mapped_type())))的返回值类型是pair<iterator,bool>,这个类型的first是iterator,所以外面再套一层*(),就得到此时插入key所对应的键值对(可能是新创建的也可能是已经存在的),再去访问该键值对的second元素–value。

在元素访问时,有一个与operator[]类似的操作at()函数(该函数不常用),都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。

当使用map来统计数据个数时,就是用operator[]来完成的。

vector<string> arr = { "苹果", "西瓜", "香蕉", "橙子", "草莓", "西瓜", "香蕉" };
map<string, int> count_map;
//方法1 map和multimap都可以用这个方法统计次数
for (auto& e : arr)
{
    map<string, int>::iterator it = count_map.find(e);
    if (it == count_map.end())
    {
        //不在就插入
        count_map.insert(pair<string, int>(e, 1));
    }
    else
    {
        it->second += 1;
    }
}
//方法2 map可以统计次数
for (size_t i = 0; i < arr.size(); i++)
{
    ++(count_map[arr[i]]);
}
for (auto e : arr)
{
    (count_map[e])++;
}
for (auto kv : count_map)
{
    cout << kv.first << "==>" << kv.second << endl;
}
----------------------------
count_map["火龙果"] = "1";//这个就是插入+修改

multimap不支持operator[]

OJ题

349.两个数组的交集

利用set一步完成去重+排序,这个解题思路的时间复杂度为 O ( N ) O(N) O(N),效率极高。可以直接用迭代器区间来初始化set。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        // 1、去重+排序
        //用set对每个数组进行去重
        set<int> s1(nums1.begin(), nums1.end());
        set<int> s2(nums2.begin(), nums2.end());
        //用set来访问数据 就相当于是按升序排序了

        // 2、找值
        //值相等就是交集值,同时++;不相等,小的++;有一个结束就结束了
        auto it1 = s1.begin();
        auto it2 = s2.begin();
        vector<int> ret;
        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 == *it2)
            {
                ret.push_back(*it1);
                it1++;
                it2++;
            }
            else if(*it1 < *it2)
                it1++;
            else it2++;
        }
        return ret;
    }
};

692.前K个高频单词

此题需要注意的是sort()底层用的是快排,而快排是不稳定的,题目要求当V相同时,K应为升序进行输出。若直接用sort进行排序,K可能变成降序,所以改用stable_sort。

另一种思路是改变仿函数的判断逻辑,first值相同的情况下,手动让second按照升序来比较大小。

class Solution {
public:
    struct Compare
    {
        //按照降序来排列
        bool operator()(const pair<int, string>& left, const pair<int, string>& right)
        {
            return left.first > right.first 
            || (left.first == right.first && left.second < right.second);//通过改变比较逻辑可以用sort
            // return left.first > right.first; //这个要配合stable_sort来使用
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        //先用用<单词,单词出现次数>构建键值对
        map<string, int> countMap;
        for(auto& str : words)
        {
            (countMap[str])++;
        }

        //按照次数来进行排序
        vector<pair<int, string>> v;
        for(auto& kv : countMap)
        {
            v.push_back(make_pair(kv.second, kv.first));
        }

        sort (v.begin(), v.end(), Compare()); //sort是用的快排,是不稳定的
        //sort排序是不稳定的,此处要用stable_sort
        // stable_sort (v.begin(), v.end(), Compare());

        vector<string> ret;
        for(size_t i = 0; i < k; i++)
        {
            ret.push_back(v[i].second);
        }
        return ret;
    }
};

猜你喜欢

转载自blog.csdn.net/m0_61780496/article/details/129519271