C++ map set

一.关联式容器

在初期学过的vector、list、deque、forward_list(C++11)等,底层都为线性序列的序列式容器。

关联式容器是用来存储数据的,于序列式容器不同的是,里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。

二.键值对

键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该单词,在词典中就可以找到与其对应的中文含义。

在SGI-STL中关于键值对的定义:

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)
	{}
};

三.set

set介绍

1.set是按照一定次序存储元素的容器。

2.在set中,元素的value也表示它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能再容器中修改(元素总是const),但是可以从容器中插入或删除它们。

3.在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。

4.set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。

5.set在底层是用二叉搜索树(红黑树)实现的。

6.set中插入元素时,只需要插入value即可,不需要构造键值对。

7.set中的元素不可以重复(因此可以使用set去重)。

8.set中的元素默认按照小于来比较。

9.set中查找某个元素,时间复杂度为O(logn)。

10.set中的元素不允许修改。

12.与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对,因此在set容器中插入元素时,只需要插入value即可,不需要构造键值对。

set定义

    set<int> s1;//1.空的set容器

    set<int> s2(s1);//2.用已有的s1拷贝构造s2

    string str("bear");
    set<char> s3(str.begin(), str.end());//3.用迭代器区间构造

set使用

插入删除操作

insert(x)当容器中没有等价元素的时候,将元素 x 插入到 set 中
erase(x)删除值为 x 的 所有 元素,返回删除元素的个数
erase(pos)删除迭代器为 pos 的元素,要求迭代器必须合法
erase(first,last)删除迭代器在 [first,last) 范围内的所有元素
clear清空容器
swap交换两个容器的数据

 查找操作

count(x)返回 set 内键为 x 的元素数量
find(x)在 set 内存在键为 x 的元素时会返回该元素的迭代器,否则返回 end()
lower_bound(x)返回指向首个不小于给定键的元素的迭代器。如果不存在这样的元素,返回 end()
upper_bound(x)返回指向首个大于给定键的元素的迭代器。如果不存在这样的元素,返回 end()
empty()返回容器是否为空
size()返回容器内元素个数

 迭代器

begin()/cbegin()返回指向首元素的迭代器,其中 *begin = front
end()/cend()返回指向数组尾端占位符的迭代器,注意是没有元素的
rbegin()/crbegin()返回指向逆向数组的首元素的逆向迭代器,可以理解为正向容器的末元素
rend()/crend()返回指向逆向数组末元素后一位置的迭代器,对应容器首的前一个位置,没有元素

 以上列出的迭代器中,含有字符 c 的为只读迭代器,你不能通过只读迭代器去修改 set 中的元素的值。如果一个 set 本身就是只读的,那么它的一般迭代器和只读迭代器完全等价。只读迭代器自 C++11 开始支持。

 下列对常用的函数进行模拟:

#include<iostream>
#include<set>
using namespace std;

int main()
{
    set<int> s1;
    //insert函数
    s1.insert(1);
    s1.insert(2);
    s1.insert(3);
    s1.insert(4);
    s1.insert(5);
    s1.insert(6);
    //遍历一,范围for
    for (auto i : s1)
    {
        cout << i << " ";
    }
    cout << endl;//1 2 3 4 5 6

    //指定元素个数
    cout << s1.count(1) << endl;//1个

    //容器大小
    cout << s1.size() << endl;//6
    
    //erase函数1
    s1.erase(6);//删除元素6

    //erase函数2
    set<int>::iterator pos = s1.find(5);
    if (pos != s1.end())
    {
        s1.erase(pos);
    }

    //遍历二(正向迭代器)
    set<int>::iterator it = s1.begin();
    while (it != s1.end())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;//1 2 3 4

    //clear函数
    s1.clear();

    //容器判空
    cout << s1.empty() << endl;//1

    //遍历方式三(反向迭代器)
    set<int>::reverse_iterator rit = s1.rbegin();
    while (rit != s1.rend())
    {
        cout << *rit << " ";
        rit++;
    }
    cout << endl;//   为空
}

multiset

multiset与set的底层与函数基本相同,不同的是multiset可以允许键值冗余,也就是说multiset容器中的元素是可以重复的

四.map

map介绍

1.map是关联式容器,它按照特定的次序(按照key来比较)存储由key和值value组合而成的元素。

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

3.在内部,map中的元素总是按照键值key进行比较排序的。

4.map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,就可以找到与key对应的value)。

5.map支持下标访问,即在[]中放入key,就可以找到与key对应的value。

6.map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

map定义

    map<int, int> m1;//构造一个key和value都为int的空容器

    map<int, int> m2(m1);//拷贝m1构造一个key和value都为int的空容器

    map<int, int> m3(m2.begin(), m2.end());//用迭代器构造一个key和value都为int的空容器

map使用

map插入

在map中插入,需要同时插入key和value

再来看看map插入函数的原型

pair<iterator,bool> insert (const value_type& val);

insert函数的参数显示为value_type类型,实际上value_type就是pair类型的别名

typedef pair<const Key, T> value_type;

所以,我们向map容器插入元素时,需要用key和value构造一个pair对象,然后再将pair对象作为参数传入insert函数。

调用make_pair函数模板插入

库中会提供make_pair模板:

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

我们只需要向make_pair函数传入key和value,该函数模板会根据传入参数类型进行自动隐式推导,最终构造并返回一个对应的pair对象。

#include<iostream>
#include<map>
using namespace std;

int main()
{
    //map插入
    map<int, string> m1;
    m1.insert(make_pair(1, "one"));
    m1.insert(make_pair(2, "two"));
    m1.insert(make_pair(3, "three"));
    for (auto i : m1)
    {
        cout << "<" << i.first << "," << i.second << ">";
        cout << endl;
    }
    cout << endl;//<1,one> <2,two> <3,three>
    return 0;
}

在前面我们可以看到,insert函数是有返回值的,该返回值也是一个pair对象,该pair对象第一个成员的类型是map的迭代器类型,第二个成员的类型是一个bool类型,下面介绍具体含义:

1.若带插入元素的键值key在map当中不存在,则insert函数插入成功,并返回插入后元素的迭代器和true。

2.若待插入元素的键值key在map当中已经存在,则insert函数插入失败,并返回map当中键值为key的元素迭代器和false。

map查找

map查找函数的原型为:

iterator find(const key_type& k);

该函数含义为,根据所给的key值在map中进行查找,假如找到了,那么返回对应元素的迭代器;如果没找到,那么返回最后一个元素下一个位置的正向迭代器。

#include<iostream>
#include<map>
using namespace std;

int main()
{
    map<int, string> m1;
    m1.insert(make_pair(1, "one"));
    m1.insert(make_pair(2, "two"));
    m1.insert(make_pair(3, "three"));

    //map查找
    map<int, string>::iterator pos = m1.find(3);
    if (pos != m1.end())
    {
        cout << pos->second << endl;//three
    }
    return 0;
}

map删除

先来看看map删除函数的原型:

size_type erase(const key_type& k);

void erase(ietrator posision);

根据函数原型可以看到,可以根据key删除指定元素,也可以根据迭代器删除指定元素。要是根据key删除,那么返回删除元素的个数。

#include<iostream>
#include<map>
using namespace std;

int main()
{
    map<int, string> m1;
    m1.insert(make_pair(1, "one"));
    m1.insert(make_pair(2, "two"));
    m1.insert(make_pair(3, "three"));


    //map删除
    cout << m1.erase(3);//1
    map<int, string>::iterator pos = m1.find(1);
    if (pos != m1.end())
    {
        m1.erase(pos);
    }
    return 0;
}

map的[]运算符重载

map的[]运算符重载函数原型为:

mapped_type& operator[] (const key_type& k);

可以看到,[]运算符重载函数的参数是一个key值,而这个函数的返回值如下:

(*((this->insert(make_pair(k, mapped_type()))).first)).second

 我们将它进行拆解分析:

1.调用insert函数插入键值对。

2.拿出从insert函数获取到的迭代器。

3.返回该迭代器位置元素的值value。

下面来看看拆解代码:

mapped_type& operator[] (const key_type& k)
{
	//1、调用insert函数插入键值对
	pair<iterator, bool> ret = insert(make_pair(k, mapped_type()));

	//2、拿出从insert函数获取到的迭代器
	iterator it = ret.first;

	//3、返回该迭代器位置元素的值value
	return it->second;
}

再来看看具体使用场景:

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

int main()
{
	map<int, string> m1;
	m1.insert(make_pair(1, "one"));
	m1.insert(make_pair(2, "two"));
	m1.insert(make_pair(3, "three"));
	m[2] = "bear"; //修改key值为2的元素的value为bear
	m[4] = "four"; //插入键值对<4, "four">
	for (auto e : m)
	{
		cout << "<" << e.first << "," << e.second << ">" << " ";
	}
	cout << endl; //<1,one> <2,bear> <3,three> <4,four>
	return 0;
}

总结:

1.如果k不在map中,那么先插入键值对<k,V()>,然后返回该键值对中对V对象的引用。

2.如果k在map中,则返回键值为k的元素对应V对象的引用。

map迭代器

begin获取容器中第一个元素的正向迭代器
end获取容器中最后一个元素下一个位置的正向迭代器
rbegin获取容器中最后一个元素的反向迭代器
rend获取容器中第一个元素前一个位置的反向迭代器

使用方法和set的相同。

map的其它成员函数

size获取容器中元素的个数
empty判断容器是否为空
clear清空容器
swap交换两个容器中的数据
count获取容器中指定key值的元素个数

multimap

multimap与map和multiset与set的关系几乎相同,multimap也允许键值冗余

在find函数中:

map对象的find功能为:返回值为键值为key的元素的迭代器。

multimap对象的find功能为:返回底层搜索树中序的第一个键值为key的元素的迭代器。

在count函数中:

map对象的count功能为:键值为key的元素存在则返回1,不存在则返回0(find成员函数可代替)

multimap对象的count功能为:返回键值为key的元素个数(find成员函数不可代替)。

另外,由于multimap容器允许键值冗余,调用[ ]运算符重载函数时,应该返回键值为key的哪一个元素的value的引用存在歧义,因此在multimap容器当中没有实现[ ]运算符重载函数。

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值