STL中map和set的基本使用

map和set的使用

map和set属于STL中的关联式容器,与序列式容器不同,关联式容器中的数据之间存在联系,进行插入和删除操作需要调整容器内部的结构。

  • STL中的序列式容器:vector,deque,list

  • STL中的适配器模式:stack,queue

  • STL中的关联式容器:map,set

map和set的底层是一个红黑树

set的使用

set的底层是一个key模型的二叉搜索树,专门用来查找数据,判断数据在不在set中。

template <class T,class Compare=less<T>,class Alloc=allocator<T>>
class set;
  • 第一个模板参数表示set中要存放的数据类型。
  • 第二个模板参数是仿函数,用来比较大小,set的底层是一个搜索树,默认左子树的结点值都小于根,右子树的所有结点值都大于根。当Compare为greater<T>时,左子树的所有结点都大于根,右子树的所有结点都小于根,此时中序遍历是降序。set默认的less<T>中序遍历是升序。当然也可以自定义仿函数传入,根据指定的比较方式进行比较。
  • 第三个模板参数是空间配置器(内存池)
构造函数

set支持迭代器区间的构造函数,在c++11中还支持列表初始化。

template <class InputIterator>
set(InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());//迭代器区间构造
set (const set& x);//拷贝构造,set的拷贝构造是深拷贝
set (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());//c++11支持initializer_list列表构造
析构函数

set的底层是二叉搜索树,其中的结点均是在堆区开辟空间,因此set的析构函数需要进行递归delete结点

operator=

set的operator=进行的是深拷贝操作,当搜索树比较大时,深拷贝的代价也比较大,因此慎用operator=

迭代器

set的迭代器底层较复杂,但是使用很简单。其begin()是指向中序的第一个结点,迭代器的++是让该迭代器指向中序的下一个结点。

int main()
{
    set<int> s;
    set<int>::iterator it=s.begin();
    it++;
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfGiCVYZ-1667276828998)(C:\Users\19199\Desktop\c++研发\数据结构\高阶数据结构\map和set.assets\image-20221029205437024.png)]

set的迭代器支持范围for

int main()
{
    int arr[] = { 9,13,24,5,76,3,5,9 };
    set<int> s(arr, arr + sizeof(arr) / sizeof(arr[0]));
    set<int>::iterator it = s.begin();
    while (it != s.end())
        cout << *it++ << ' ';
    cout << endl;
    for (int i : s)
        cout << i << ' ';
    cout << endl;
    return 0;
}

set的反向迭代器rbegin()可以认为指向中序遍历的最后一个结点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yLqIiCFB-1667276828999)(C:\Users\19199\Desktop\c++研发\数据结构\高阶数据结构\map和set.assets\image-20221029210154942.png)]

empty,size,maxsize

empty:判断是否为空

size:set中结点的个数

maxsize:set中能存放的最大结点的个数

insert

insert支持迭代器区间插入,支持插入一个数据,支持在迭代器位置插入一个数据。

pair<iterator,bool> insert(const value_type& val);
iterator insert(iterator pos,const value_type& val);
template<class inputiterator>
void insert(inputiterator first,inputiterator last);

第一个构造函数中的pair是键值对,在STL中是一个模板类,pair的key是一个set的迭代器,value是一个bool值

template<class T1,class T2>//T1是key的类型,T2是value的类型
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){}
};
int main()
{
    set<int> s = { 1,7,4,7,9,3 };
    int x = 10;
    pair<set<int>::iterator,bool> ret=s.insert(x);
    if (ret.second == false)
    {
        cout << "ret.second=" << ret.second << endl;
        printf("插入%d失败\n", x);//原因是set不允许键值对冗余
    } 
    else
    {
        printf("插入%d成功,返回了指向%d这个结点的迭代器(存放在pair中)\n", x,x);
        cout << "ret.second=" << ret.second << endl;
        //ret.first是迭代器,不能直接使用
        cout << "*ret.first=" << *ret.first << endl;
    }
    return 0;
}

set的pair<iterator,bool> insert(const value_type& val);设置了pair作为返回值是为了和map对应。

set的iterator insert(iterator pos,const value_type& val);的返回值是指向新插入的val的迭代器。如果val在set中已经存在,那么返回指向val的迭代器。参数pos是为了加快insert的速度而传入的一个参数,如果知道val大概要插入到搜索树的哪个位置,就可以传入指向该位置的迭代器。

using namespace std;
int main()
{
    set<int> s = { 1,9,14,6,3,54,2,0 };
    set<int>::iterator ret1=s.insert(s.begin(),9);
    cout << "*ret=" << *ret1 << endl;
    for (const auto& i : s)
        cout << i << ' ';
    cout << endl;
    set<int>::iterator ret2 = s.insert(s.begin(), 4);
    cout << "*ret=" << *ret2 << endl;
    for (const auto& i : s)
        cout << i << ' ';
    cout << endl;
    return 0;
}
erase
void erase (iterator pos);//给定迭代器,删除迭代器指向的结点
bool erase (const value_type& val);//给指定值,set中存在该值,则删除对应结点,返回true.否则返回false
void erase (iterator first, iterator last);//删除一段迭代器区间

void erase (iterator pos);如果传入的迭代器不合法,在debug模式下会断言检查报错。

int main()
{
    set<int> s = { 1,9,14,6,3,54,2,0 };
    for (const auto& i : s)
        cout << i << ' ';
    cout << endl;
    s.erase(s.find(-1));//s.find()找不到-1会返回end(),end()不指向有效结点,debug模式下报错
    for (const auto& i : s)
        cout << i << ' ';
    cout << endl;
    return 0;
}
swap
void swap (set<value_type>& x);

交换2颗set底层的红黑树(实际上是交换了根节点)

int main()
{
    set<int> s1 = { 1,4,3 };
    set<int> s2 = { 9,5,0 };
    for (const auto& i : s1)
        cout << i << ' ';
    cout << endl;
    for (const auto& i : s2)
        cout << i << ' ';
    cout << endl;
    s1.swap(s2);
    for (const auto& i : s1)
        cout << i << ' ';
    cout << endl;
    for (const auto& i : s2)
        cout << i << ' ';
    cout << endl;
    return 0;
}
clear

销毁整棵树

find
const_iterator find (const value_type& val) const;
iterator       find (const value_type& val);

查找,找到了返回指向该结点的迭代器,找不到返回end(),end()不指向有效结点

int main()
{
    set<int> s = { 1,4,3 };
    auto it = s.find(2);
    if (it == s.end())
        cout << "找不到" << endl;
    else
        cout << "*it=" << *it << endl;
    return 0;
}
count

统计某一个值出现的次数,对于set,这个函数的返回值为0或1,基本无意义,对于multiset(允许键值对冗余的set),这个值有意义。

int main()
{
    set<int> s = { 1,1,2,3 };
    cout << s.count(1) << endl;
    cout << s.count(4) << endl;
    multiset<int> ms = { 1,1,2,3 };
    cout << ms.count(1) << endl;
    cout << ms.count(3) << endl;
    return 0;
}
lower_bound和upper_bound

在set中有lower_bound和upper_bound,在算法库中也有通用的lower_bound和upper_bound.

算法库中的lower_bound:

template<class forwarditerator,class T>
forwarditerator lower_bound(forwarditerator first,forwarditerator last,const T& val);

在一个迭代器区间[first,last)中找到第一个大于等于val的位置,并返回该位置的迭代器,如果找不到,返回last。lower_bound要求[first,last)的元素按照统一的标准进行了排序

int main()
{
    vector<int> v = { 2,4,7,9,18 };
    auto it = lower_bound(v.begin(), v.end(), 6);
    cout << *it << endl;
    return 0;
}

可以设置Compare,让lower_bound在[first,last)中找到第一个小于等于val的位置,当Compare是greater时,lower_bound是找第一个小于等于val的位置

template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);

算法库中的upper_bound:

upper_bound与lower_bound的唯一区别在于upper_bound是找第一个大于val的位置,当Compare为greater时,upper_bound是找第一个小于val的位置。

set的lower_bound和upper_bound

iterator lower_bound (const value_type& val);
const_iterator lower_bound (const value_type& val) const;
int main()
{
    //基于set的Compare是less<T>的前提
    set<int> s={5,8,2,1,3,4};
    set<int>::iterator it=s.lower_bound(2);//第一个>=2位置的迭代器,指向2的迭代器
    cout<<*it<<endl;
    it=s.upper_bound(2);//第一个大于2位置的迭代器,指向3的迭代器
    cout<<*it<<endl;
    return 0;
}

若set的Compare是greater<T>则lower_bound(int val)返回第一个<=val的迭代器,upper_bound返回第一个<val的迭代器。

equal_range
pair<const_iterator,const_iterator> equal_range (const value_type& val) const;
pair<iterator,iterator>             equal_range (const value_type& val);

在set中找所有等于val的值,返回这段迭代器区间。equal_range一般适用于multiset

int main()
{
    multiset<int> ms={1,5,3,4,3,6,3};
    pair<multiset<int>::iterator,multiset<int>::iterator> ret=ms.equal_range(3);
    multiset<int>::iterator it=ret.first;
    while(it!=ret.last)
    	cout<<*it++<<endl;
    return 0;
}

multiset

multiset与set的使用基本一致,multiset允许键值对冗余,multiset有一个重载的erase会删除所有等于val的值

int main()
{
    multiset<int> ms={3,1,6,3,5,5,3,1,2};
    for(const auto& i:ms)
        cout<<i<<' ';
    cout<<endl;
    ms.erase(3);
    ms.erase(5);
    for(const auto& i:ms)
        cout<<i<<' ';
    cout<<endl;
    return 0;
}

map的使用

map与set的不同之处是map的底层红黑树的结点存的是结构体,map的模型是key-value模型。

template <class Key,class T,class Compare = less<Key>,class Alloc = allocator<pair<const Key,T>>>
class map;

Key是key的类型,T是value的类型,map底层是按照key的大小进行排序的,默认是key大于根节点的都在右边,小于根节点的都在左边。若Compare为greater<Key>,则相反。

int main()
{
    map<string, string,greater<string>> m;
    m.insert(make_pair("string", "字符串"));
    m.insert(make_pair("sort", "排序"));
    m.insert(make_pair("vector", "向量"));
    m.insert(make_pair("list", "双链表"));
    m.insert(make_pair("deque", "双端队列"));
    for (const auto& e : m)
    {
        //e.first对应key,e.second对应value
        cout << e.first << "  " << e.second << endl;
    }
    map<string, string>::iterator it = m.begin();
    while (it != m.end())
    {
        cout << it->first << "  " << it->second << endl;
        it++;
    }
    return 0;
}
operator[]

map支持operator[],可以根据key得到value.使用起来十分方便

int main()
{
    map<string, string> m;
    m.insert(make_pair("string", "字符串"));
    m.insert(make_pair("sort", "排序"));
    m.insert(make_pair("vector", "向量"));
    m.insert(make_pair("list", "双链表"));
    m.insert(make_pair("deque", "双端队列"));
    cout<<m["string"]<<' ';
    cout<<m["sort"]<<' ';
    cout<<m["vector"]<<' ';
    cout<<m["list"]<<' ';
    cout<<m["deque"]<<' ';
    return 0;
}

map的operator[]如果[]中的key不存在,会创建一个pair并插入.

int main()
{
    map<string, int> countmap;
    string str[] = { "苹果","梨子","菠萝", "香蕉", "香蕉", "苹果", "菠萝", "梨子", "苹果", "苹果" };
    for (const string& s : str)
        countmap[s]++;//第一次创建的时候相当于insert(pair<string,int>("苹果",int())),返回value的引用
    for (const auto& s : countmap)
        cout << s.first << "  " << s.second << endl;
    return 0;
}
int main()
{
    map<string, int> countmap;
    string str[] = { "苹果","梨子","菠萝", "香蕉", "香蕉", "苹果", "菠萝", "梨子", "苹果", "苹果" };
    for (const string& s : str)
        cout << countmap[s] << endl;
    for (const auto& s : countmap)
        cout << s.first << "  " << s.second << endl;
    return 0;
}
0
0
0
0
0
0
0
0
0
0
菠萝  0
梨子  0
苹果  0
香蕉  0
insert和erase

map的insert需要插入一个键值对pair(实际上就是一个结构体),erase只需要提供key就能从红黑树中删除这个key对应的结构体。

int main()
{
    map<string,int> m;
    m.insert(pair<string,int>("张三",18));
    m.insert(make_pair("李四",20));
    m.erase("张三");
    return 0;
}

make_pair是一个函数模板,由于函数模板可以自动推导类型,所以在向map中插入结构体时,经常使用make_pair.

template <class T1, class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
    return pair<T1,T2>(x,y);
}

multimap

map不允许键值对冗余,而multimap允许键值对冗余。multimap没有operator[]

int main()
{
    multimap<string,int> mp;
    mp.insert(make_pair("张三",18));
    mp.insert(make_pair("张三",19));
    mp.insert(make_pair("李四",20));
    auto it=mp.find("张三");
    it->second=22;
    it++;
    it->second=25;
    for(const auto& p:mp)
        cout<<p.first<<' '<<p.second<<endl;
    return 0;
}

用STL的map解决一道c语言处理起来比较麻烦的题:138. 复制带随机指针的链表 - 力扣(LeetCode)

class Solution {
public:
    Node* copyRandomList(Node* head) {
        //1.复制链表并建立key-value映射
        Node* tmp=new Node(0);
        Node* copy=tmp;
        Node* cur=head;
        map<Node*,Node*> mp;
        while(cur)
        {
            tmp->next=new Node(cur->val);
            tmp=tmp->next;
            mp.insert(make_pair(cur,tmp));//建立原链表结点与拷贝链表结点的映射
            cur=cur->next;
        }
        //2.根据key-value确定复制的链表的random
        tmp=copy->next;
        cur=head;
        while(cur)
        {
            tmp->random=mp[cur->random];
            tmp=tmp->next;
            cur=cur->next;
        }
        Node* ans=copy->next;
        delete copy;
        return ans;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值