C++ map和set

一.关联式容器和序列式容器

   关联式容器和序列容器都是C++ STL中的容器,它们主要的区别在于其存储内部元素的方式不同。

1.1 序列容器

   序列容器是按照元素插入的顺序来存储元素的,也就是说,元素在容器中的位置与其被插入的先后顺序是一致的。例如,vector、list 和deque都是序列容器。

1.2 关联式容器 

   而关联式容器不同,它们是通过比较关键字来对元素进行有序存储的。关键字可以是一个简单数据类型,例如整数或字符串,也可以是一个自定义的结构体或类,元素是按照关键字大小的顺序进行存储的。例如,set、map、multiset和multimap都是关联式容器。

1.3 区别 

  因此,序列容器和关联式容器的最大区别在于内部元素的顺序

一般而言,关联式容器的检索效率比序列容器效率更高。

  此外,在实现上,关联式容器一般采用红黑树或哈希表进行元素的存储和访问,而序列容器只需要链表或数组即可实现。

  两种容器的插入、删除和查找操作的时间复杂度也有所不同,建议根据实际问题的需求来选择使用何种容器。

二 键值对

  2.1 键值对的概念

   用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息,也称key-value模型。

  比如:现在要创建一个英汉翻译的字典,那该字典中必然 有英文单词与其对应的中文含义,而且,英文单词与其中文含义是有些必定联系的,即通过该应 该单词,在词典中就可以找到与其对应的一种或多种中文含义。

 2.2 STL对键值对的表示

在STL中 ,也存在键值对:

其在内部大概是这样定义的:

#include<iostream>
using namespace std;

template <class T1, class T2>
struct pair  //差不多就是有两个元素的结构体
{

	typedef T1 first_type;
	typedef T2 second_type;
	T1 first; //通过first和second寻找到俩个值
	T2 second;
	pair()
	  : first(T1())
	   ,second(T2())
	{}
	pair(const T1& a, const T2& b) 
	  : first(a)
	  , second(b)
	{}
};

定义在iostream库中

#include<iostream>
using namespace std;

int main()
{
	pair<int, int> p1;
	p1.first = 1;
	p1.second = 2;
	cout << p1.first <<endl<< p1.second << endl;
	return 0;
}

 2.3 键值对的作用

 在程序中,我们大多数在另一种数据结构中使用键值对,也就是说 我们在使用时大多将键值对作为一个类的元素,从而进行某种操作,但很少独自使用。

三.关联式容器的类型

   根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。

  这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。我们这篇博客将介绍map和set树形结构关联式容器。

3.1 set

3.1.1 set的特点

 包含在#include<set> 库中

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

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

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

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

注意:

1. 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对,因此set只能只需插入value即可。

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

3. 使用set的迭代器遍历set中的元素,可以得到有序序列

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

3.1.2 set的模板参数列表

T: set中存放元素的类型,实际在底层存储的键值对。

Compare:set中元素默认按照小于来比较。

Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理。

3.1.3  set的构造

函数声明 功能介绍
set (const Compare& comp = Compare(), const Allocator& = Allocator() );构造空的set
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); 用[first, last)区 间中的元素构造 set
set ( const set& x);

set的拷贝构造

 代码示例

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

int main()
{
    //默认构造
	set<int> s1;

	//使用vector迭代器构造
	vector<int> v = { 1,4,5,1231,21 };
	set<int> s2(v.begin(), v.end());

	//使用拷贝构造
	set<int> s3(s2); 

	return 0;
}

3.1.4 set的迭代器

函数声明功能介绍
iterator begin()    返回set中起始位置元素的迭代器
iterator end()  返回set中最后一个元素后面的迭代器
const_iterator cbegin() const    返回set中起始位置元素的const迭代器返回set中起始位置元素的const迭代器
const_iterator cend() const 返回set中最后一个元素后面的const迭代器
reverse_iterator rbegin() 返回set第一个元素的反向迭代器,即end
reverse_iterator rend()返回set最后一个元素下一个位置的反向迭代器,即rbegin
const_reverse_iterator crbegin() const  返回set第一个元素的反向const迭代器,即cend
const_reverse_iterator crend() const 返回set最后一个元素下一个位置的反向const迭代器,即crbegin

这个就没什么好介绍的了,老生常谈了,但给大家看一下这个。

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

int main()
{
	vector<int> v = { 1,4,5,1231,21 ,2,3, 100,9};
	set<int> s(v.begin(), v.end());

	for (auto& x : s)
	{
		cout << x << ' ';
	}
	cout << endl;

	return 0;
}

我们可以证明 set在内部的存储是有序的,其内置元素类型会在其自动排序,自定义类型我们可以手动传入函数排序(一会细讲)。  

 算了 示例一下吧:

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

int main() 
{
    set<int> s = { 1, 2, 3, 4, 5 };

    // 使用正向迭代器遍历set
    cout << "正向遍历: ";
    for (auto it = s.begin(); it != s.end(); ++it) 
    {
        cout << *it << " ";
    }
    cout <<endl;

    // 使用反向迭代器遍历set
    cout << "反向遍历: ";
    for (auto it = s.rbegin(); it != s.rend(); ++it) 
    {
       cout << *it << " ";
    }
    cout << endl;

    // 使用const迭代器遍历set
    cout << "const遍历: ";
    for (auto it = s.cbegin(); it != s.cend(); ++it) 
    {
       cout << *it << " ";
    }
   cout << std::endl;

    // 使用const反向迭代器遍历set
    cout << "const反向遍历: ";
    for (auto it = s.crbegin(); it != s.crend(); ++it) 
    {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}

结果为:

3.1.5 set的容量函数 

函数声明功能介绍
bool empty ( ) const检测set是否为空,空返回true,否则返回true
size_type size() const返回set中有效元素的个数
#include <iostream>
#include <set>
using namespace std;

int main() 
{
    set<int> s1;

    if (s1.empty())
    {
        cout << "set为空" << endl;
    }
    else 
    {
        cout << "set不为空" << endl;
    }

    s1.insert(1);
    s1.insert(2);
    s1.insert(3);

    if (s1.empty())
    {
        cout << "set为空" << endl;
    }
    else 
    {
        cout << "set不为空" << endl;
    }

    cout << "set中元素的个数为: " << s1.size() << endl;

    return 0;
}

 代码结果为:

3.1.6 set的增删查改函数

函数声明功能介绍
pair<iterator,bool> insert ( const value_type& x )    在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>
void erase ( iterator position ) 删除set中position位置上的元素
size_type erase ( const key_type& x ) 删除set中值为x的元素,返回删除的元素的个数
void erase ( iterator first, iterator last )删除set中[first, last)区间中的元素
void swap ( set<Key,Compare,Allocator>& st );  交换set中的元素
void clear ( )将set中的元素清空
iterator find ( const key_type& x ) const返回set中值为x的元素的位置
size_type count ( const key_type& x ) const 返回set中值为x的元素的个数

代码示例:

insert 有多种重载,这里只选择最常用的几种重载。

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

int main() 
{
    set<int> s;
  
    // 插入单个元素
    s.insert(1);
    for (auto& x : s)
    {
        cout << x << ' ';
    }
    cout << endl;


    // 插入多个元素
    s.insert({ 2, 3, 4 });
    for (auto& x : s)
    {
        cout << x << ' ';
    }
    cout << endl;


    // 插入范围内的元素
    set<int> anotherSet = { 5, 6, 7 };
    s.insert(anotherSet.begin(), anotherSet.end());

    // 输出set中的元素
    for (auto& x : s)
    {
        cout << x << ' ';
    }
    cout << endl;

    return 0;
}

 

set同样

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

int main() 
{
    std::set<int> s = { 1, 2, 3, 4, 5 };

    // 删除指定元素
    s.erase(3);
    for (auto& x : s)
    {
        cout << x << ' ';
    }
    cout << endl;
    // 删除指定范围内的元素 从开始一直到4之前结束
    s.erase(s.begin(), s.find(4));

    // 输出set中的元素
    for (auto& x : s)
    {
        cout << x << ' ';
   }
    cout << endl;

    return 0;
}

代码结果为:

3.2 map

其中 Key和T 是一个键值对  

Compare:map中元素默认按照小于来比较。

Alloc:map中元素空间的管理方式,使用STL提供的空间配置器管理。

3.2.1 map的概念和特点

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

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的 内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type,也就是说,在内部两个变量将合为一个pair类型

3. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

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

 3.2.2 map的模板参数列表

 

其中 Key和T 是一个键值对  

Compare:map中元素默认按照小于来比较。

Alloc:map中元素空间的管理方式,使用STL提供的空间配置器管理。

 3.2.3  map的构造

函数声明

功能介绍
map (const Compare& comp = Compare(), const Allocator& = Allocator() );构造空的map
map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator(());用[first, last)区间中的元素构造map
map( const map<Key,Compare,Allocator>& x);map的拷贝构造

如: 

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

int main()
{
	//1.默认构造函数:创建一个空的map对象。
	map<int, string> myMap;


	//通过传入键值对来创建map对象。
	map<int, string> myMap = { {1, "apple"}, {2, "banana"}, {3, "orange"} };

	//2.用迭代器构造:通过使用迭代器范围来构造map对象。
	vector<pair<int, string>> vec = { {1, "apple"}, {2, "banana"}, {3, "orange"} };
	map<int, string> myMap(vec.begin(), vec.end());


	//3.拷贝构造函数:通过拷贝另一个map对象来创建一个新的map对象。
	map<int, string> originalMap = { {1, "apple"}, {2, "banana"}, {3, "orange"} };
	map<int, string> copiedMap(originalMap);
return 0;
}

3.2.4 map的迭代器

函数声明功能介绍
iterator begin()    返回map中起始位置元素的迭代器
iterator end()  返回map中最后一个元素后面的迭代器
const_iterator cbegin() const    返回set中起始位置元素的const迭代器返回map中起始位置元素的const迭代器
const_iterator cend() const 返回map中最后一个元素后面的const迭代器
reverse_iterator rbegin() 返回map第一个元素的反向迭代器,即end
reverse_iterator rend()返回map最后一个元素下一个位置的反向迭代器,即rbegin
const_reverse_iterator crbegin() const  返回map第一个元素的反向const迭代器,即cend
const_reverse_iterator crend() const 返回map最后一个元素下一个位置的反向const迭代器,即crbegin

这真老生常谈了,我真不想给大家示例了:

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

int main() 
{
    map<int, std::string> Map = { {1, "apple"}, {2, "banana"}, {3, "orange"} };

    // 正向迭代器
    map<int, string>::iterator it;
    cout << "正向迭代器:" << endl;
    for (it = Map.begin(); it != Map.end(); ++it) 
    {
        cout << it->first << ": " << it->second << endl;
    }

    // 逆向迭代器
    map<int, string>::reverse_iterator rit;
    cout << "逆向迭代器:" << endl;
    for (rit =Map.rbegin(); rit != Map.rend(); ++rit) {
        cout << rit->first << ": " << rit->second << endl;
    }

    // 常量迭代器
    map<int, string>::const_iterator cit;
    cout << "常量迭代器:" << endl;
    for (cit = Map.cbegin(); cit != Map.cend(); ++cit) 
    {
        cout << cit->first << ": " << cit->second <<endl;
    }

    return 0;
}

代码结果为: 

3.2.5 map的容量和访问函数

函数声明功能简介
bool empty ( ) const检测map中的元素是否为空,是返回 true,否则返回false
size_type size() const返回map中有效元素的个数
 mapped_type& operator[] (const key_type& k)返回去key对应的value

 前两个我们不做解答,大家都懂,直接上代码。

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

int main()
{
    map<int, string> Map = { {1, "apple"}, {2, "banana"}, {3, "orange"} };

    map<int, int> Map2;

    if (Map.empty()) 
    {
        cout << "Map is empty" << endl;
    }
    else {
       cout << "Map is not empty" << endl;
    }
 
    if (Map2.empty())
    {
        cout << "Map2 is empty" << endl;
    }
    else {
        cout << "Map2 is not empty" << endl;
    }

    cout << "Map size: " << Map.size() <<endl;
    cout << "Map2 size: " << Map2.size() << endl;
    return 0;
}

代码结果为:

 我们都知道,在Vector中,[]只是单纯的进行一个下标访问,同时还需要注意下表越界。

但在map中,我们不仅不用注意下标访问题,还能用[]直接插入键值对

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

int main() 
{
    map<int, string> Map;


    // 使用operator[]函数插入键值对
    Map[1] = "apple";
    Map[2] = "banana";
    Map[3] = "orange";

    // 使用size函数获取map的大小
    cout << "Map size: " <<Map.size() << endl;

    Map[4] = "pear";


    cout << "Map size: " << Map.size() << endl;
    // 使用operator[]函数访问map中的值
    cout << "Value at key 2: " << Map[4] << endl;

    return 0;
}

3.2.6 map的增删查改函数

函数声明        功能简介
pair<iterator,bool> insert ( const value_type& x )在map中插入键值对x,注意x是一个键值 对,返回值也是键值对:iterator代表新插入 元素的位置,bool代表释放插入成功
void erase ( iterator position )删除position位置上的元素
size_type erase ( const key_type& x )删除键值为x的元素
void erase ( iterator first, iterator last )删除[first, last)区间中的元素
void swap ( map& mp )交换两个map中的元素
void clear ( )将map中的元素清空
iterator find ( const key_type& x )在map中插入key为x的元素,找到返回该元 素的位置的迭代器,否则返回end
const_iterator find ( const key_type& x ) const在map中插入key为x的元素,找到返回该元 素的位置的const迭代器,否则返回cend
size_type count ( const key_type& x ) const返回key为x的键值在map中的个数,注意 map中key是唯一的,因此该函数的返回值 要么为0,要么为1,因此也可以用该函数来 检测一个key是否在map中
#include <iostream>
#include <map>
#include<string>

using namespace std;

int main() 
{
    map<int, string> Map;

    // 使用insert函数插入键值对
    Map.insert(make_pair(1, "apple"));
    Map.insert(make_pair(2, "banana"));
    Map.insert(make_pair(3, "orange"));

    // 打印插入后的map
    cout << "Map after insert:" <<endl;
    for (const auto& pair : Map) 
    {
        cout << pair.first << ": " << pair.second << endl;
    }

    // 使用erase函数删除键值对
    Map.erase(2);

    // 打印删除后的map
    cout << "Map after erase:" << endl;

    for (const auto& pair : Map) 
    {
        cout << pair.first << ": " << pair.second << endl;
    }

    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值