C++set和map

c++ set用法详解

set

set就是集合,STL的set用二叉树实现,集合中的每个元素只出现一次(参照数学中集合的互斥性),并且是排好序的(默认按键值升序排列)

访问元素的时间复杂度是

1. set 是按照一定次序存储元素的容器
2. set 中,元素的 value 也标识它 (value 就是 key ,类型为 T) ,并且每个 value 必须是唯一的。
set 中的元素不能在容器中修改 ( 元素总是 const) ,但是可以从容器中插入或删除它们。
3. 在内部, set 中的元素总是按照其内部比较对象 ( 类型比较 ) 所指示的特定严格弱排序准则进行
排序。
4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对
子集进行直接迭代。
5. set 在底层是用二叉搜索树 ( 红黑树 ) 实现的。

 

注意:
1. map/multimap 不同, map/multimap 中存储的是真正的键值对 set 中只放
value ,但在底层实际存放的是由 构成的键值对。
2. set 中插入元素时,只需要插入 value 即可,不需要构造键值对。
3. set 中的元素不可以重复 ( 因此可以使用 set 进行去重 )
4. 使用 set 的迭代器遍历 set 中的元素,可以得到有序序列
5. set 中的元素默认按照小于来比较
6. set 中查找某个元素,时间复杂度为: log_2 n
7. set 中的元素不允许修改 ( 为什么 ?)
8. set 中的底层使用二叉搜索树 ( 红黑树 ) 来实现

在c++中,set的头文件是#include 

set具有迭代器set::iterator i 定义一个迭代器,名为i 可以把迭代器理解为C语言的指针 

set常用操作 

set q;     //以int型为例 默认按键值升序
set> p;  //降序排列 
int x;
q.insert(x);	//将x插入q中
q.erase(x);		//删除q中的x元素,返回0或1,0表示set中不存在x
q.clear();		//清空q
q.empty();		//判断q是否为空,若是返回1,否则返回0
q.size();		//返回q中元素的个数
q.find(x);		//在q中查找x,返回x的迭代器,若x不存在,则返回指向q尾部的迭代器即 q.end()
q.lower_bound(x); //返回一个迭代器,指向第一个键值不小于x的元素
q.upper_bound(x); //返回一个迭代器,指向第一个键值大于x的元素

q.rend();		  //返回第一个元素的的前一个元素迭代器
q.begin();		  //返回指向q中第一个元素的迭代器

q.end();		 //返回指向q最后一个元素下一个位置的迭代器
q.rbegin();		 //返回最后一个元素

 set单元素应用

#include
#include
using namespace std;

void map_test1()
{
	set q;   //默认按升序排列 
	q.insert(5);
	q.insert(5);
	q.insert(5);
	cout << "q.size " << q.size() << endl;   //输出 1 ,在set插入中相同元素只会存在一个

	q.clear(); //清空set
	cout << "q.size " << q.size() << "\n\n";

	q.insert(4);
	q.insert(4);
	q.insert(3);
	q.insert(3);
	q.insert(2);
	q.insert(1);

	cout << "lower_bound " << *q.lower_bound(3) << endl;  //返回3 
	cout << "upper_bound " << *q.upper_bound(3) << "\n\n";  //返回4 

	set::iterator i;
	for (i = q.begin(); i != q.end(); i++)   //set的遍历 
		cout << *i << " ";				   //输出1 2 3 4,可见自动按键值排序 
	cout << endl;

	q.erase(4);  //删除q中的 4 

	for (i = q.begin(); i != q.end(); i++)  //再次遍历set 只输出 1 2 3 
		cout << *i << " ";
	cout << "\n\n";


	set> p;  //降序排列 
	p.insert(1);
	p.insert(2);
	p.insert(3);
	p.insert(4);
	p.insert(5);
	for (i = p.begin(); i != p.end(); i++)
		cout << *i << " ";
	cout << endl;
}

int main()
{
	map_test1();

	return 0;
}

 输出结果:

C++set和map_第1张图片

set多元素应用(结构体) 

#include
#include
using namespace std;
struct node {
	int a, b;
	bool operator< (const node W)const
	{
		return a > W.a;  //按a的值升序 
	}
}t;

void map_test2()
{
	set q;
	t.a = 1;
	t.b = 2;
	q.insert(t);

	t.a = 4;
	t.b = 2;
	q.insert(t);

	t.a = 3;
	t.b = 5;
	q.insert(t);

	set::iterator i;
	for (i = q.begin(); i != q.end(); i++)
	{
		t = *i;
		cout << t.a << " " << t.b << endl;
	}
}
int main()
{
	map_test2();
	return 0;
}

 输出结果:

C++set和map_第2张图片

 map简介

1. map 是关联容器,它按照特定的次序 ( 按照 key 来比较 ) 存储由键值 key 和值 value 组合而成的元
素。
2. map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的
内容。键值 key 和值 value 的类型可能不同,并且在 map 的内部, key value 通过成员类型
value_type 绑定在一起,为其取别名称为 pair:
typedef pair value_type;
3. 在内部, map 中的元素总是按照键值 key 进行比较排序的。
4. map 中通过键值访问单个元素的速度通常比 unordered_map 容器慢,但 map 允许根据顺序
对元素进行直接迭代 ( 即对 map 中的元素进行迭代时,可以得到一个有序的序列 )
5. map 支持下标访问符,即在 [] 中放入 key ,就可以找到与 key 对应的 value
6. map 通常被实现为二叉搜索树 ( 更准确的说:平衡二叉搜索树 ( 红黑树 ))

map的功能

自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。 

使用map

使用map得包含map类所在的头文件

#include   //注意,STL头文件没有扩展名.h

map对象是模板类,需要关键字和存储对象两个模板参数:

std:map personnel;

这样就定义了一个用int作为索引,并拥有相关联的指向string的指针.

为了使用方便,可以对模板类进行一下类型定义,

typedef map UDT_MAP_INT_CSTRING;

UDT_MAP_INT_CSTRING enumMap;
 

 map的构造函数

map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map: 

map的基本操作函数: 

 begin()                 返回指向map头部的迭代器

 clear()               删除所有元素

 count()                返回指定元素出现的次数, (帮助评论区理解: 因为key值不会重复,所以只能是                                                                                                                                                   1 or 0)

 empty()               如果map为空则返回true

 end()                  返回指向map末尾的迭代器

 equal_range()    返回特殊条目的迭代器对

 erase()               删除一个元素

 find()                 查找一个元素

 get_allocator()  返回map的配置器

 insert()              插入元素

 key_comp()      返回比较元素key的函数

 lower_bound()   返回键值>=给定元素的第一个位置

 max_size()       返回可以容纳的最大元素个数

 rbegin()            返回一个指向map尾部的逆向迭代器

 rend()              返回一个指向map头部的逆向迭代器

 size()               返回map中元素的个数

 swap()             交换两个map

 upper_bound()    返回键值>给定元素的第一个位置

 value_comp()     返回比较元素value的函数
 

例如:

#include 
#include 
void TestMap()
{
 map m;
 // 向map中插入元素的方式:
 // 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
 m.insert(pair("peach", "桃子"));
 // 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
 m.insert(make_pair("banan", "香蕉"));
 
 // 借用operator[]向map中插入元素
    /*
 operator[]的原理是:
  用构造一个键值对,然后调用insert()函数将该键值对插入到map中
  如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
  如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
  operator[]函数最后将insert返回值键值对中的value返回
 */
    // 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引
用结果,
 m["apple"] = "苹果";
 // key不存在时抛异常
 //m.at("waterme") = "水蜜桃";
 cout << m.size() << endl;
 // 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
 for (auto& e : m)
 cout << e.first << "--->" << e.second << endl;
 cout << endl;
 // map中的键值对key一定是唯一的,如果key存在将插入失败
 auto ret = m.insert(make_pair("peach", "桃色"));
 if (ret.second)
 cout << "不在map中, 已经插入" << endl;
 else
 cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
<< ret.first->second <<" 插入失败"<< endl;
 // 删除key为"apple"的元素
 m.erase("apple");
 if (1 == m.count("apple"))
 cout << "apple还在" << endl;
 else
 cout << "apple被吃了" << endl;
}
【总结】
1. map 中的的元素是键值对
2. map 中的 key 是唯一的,并且不能修改
3. 默认按照小于的方式对 key 进行比较
4. map 中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map 的底层为平衡搜索树 ( 红黑树 ) ,查找效率比较高 O(log_2 N)
6. 支持 [] 操作符, operator[] 中实际进行插入查找。

multiset

multiset的介绍 

1. multiset 是按照特定顺序存储元素的容器,其中元素是可以重复的。
2. multiset 中,元素的 value 也会识别它 ( 因为 multiset 中本身存储的就是 组成
的键值对,因此 value 本身就是 key key 就是 value ,类型为 T). multiset 元素的值不能在容器
中进行修改 ( 因为元素总是 const ) ,但可以从容器中插入或删除。
3. 在内部, multiset 中的元素总是按照其内部比较规则 ( 类型比较 ) 所指示的特定严格弱排序准则
进行排序。
4. multiset 容器通过 key 访问单个元素的速度通常比 unordered_multiset 容器慢,但当使用迭
代器遍历时会得到一个有序序列。
5. multiset 底层结构为二叉搜索树 ( 红黑树 )
注意:
1. multiset 中再底层中存储的是 的键值对
2. mtltiset 的插入接口中只需要插入即可
3. set 的区别是, multiset 中的元素可以重复, set 是中 value 是唯一的
4. 使用迭代器对 multiset 中的元素进行遍历,可以得到有序的序列
5. multiset 中的元素不能修改
6. multiset 中找某个元素,时间复杂度为 $O(log_2 N)$
7. multiset 的作用:可以对元素进行排序

multiset的使用

#include 
void TestSet()
{
  int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
 
 // 注意:multiset在底层实际存储的是的键值对
 multiset s(array, array + sizeof(array)/sizeof(array[0]));
 for (auto& e : s)
 cout << e << " ";
 cout << endl;
 return 0;
}

 multimap

multimap的介绍 

1. Multimaps 是关联式容器,它按照特定的顺序,存储由 key value 映射成的键值对
value> ,其中多个键值对之间的 key 是可以重复的。
2. multimap 中,通常按照 key 排序和惟一地标识元素,而映射的 value 存储与 key 关联的内
容。 key value 的类型可能不同,通过 multimap 内部的成员类型 value_type 组合在一起,
value_type 是组合 key value 的键值对 :
typedef pair value_type ;
3. 在内部, multimap 中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对
key 进行排序的。
4. multimap 通过 key 访问单个元素的速度通常比 unordered_multimap 容器慢,但是使用迭代
器直接遍历 multimap 中的元素可以得到关于 key 有序的序列。
5. multimap 在底层用二叉搜索树 ( 红黑树 ) 来实现。

 

注意: multimap map 的唯一不同就是: map 中的 key 是唯一的,而 multimap key 是可以
重复的

multimap的使用 

multimap 中的接口可以参考 map ,功能都是类似的。
注意:
1. multimap 中的 key 是可以重复的。
2. multimap 中的元素默认将 key 按照小于来比较
3. multimap 中没有重载 operator[] 操作 ( 同学们可思考下为什么 ?)
4. 使用时与 map 包含的头文件相同:

 

底层结构

前面对 map/multimap/set/multiset 进行了简单的介绍,在其文档介绍中发现,这几个容器有个
共同点是: 其底层都是按照二叉搜索树来实现的 ,但是二叉搜索树有其自身的缺陷,假如往树中
插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成 O(N) ,因此
map set 等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

 

你可能感兴趣的:(C++学习之路,c++,开发语言)