【C++取经之路】set的详细介绍及其使用

目录

前言

关于set

 键值对

set的介绍 

set的使用


前言

因为set和map中涉及到一些新的概念,所以会在前面做一些必要的铺垫以便后面的叙述,并不是说离题了。

关于set

再谈容器,先问一个问题:何为容器?《STL源码剖析》一书中是这么解释的——容器,置物之所也。根据“数据在容器中的排列”特性,容器可以分为序列式容器和关联式容器两种。

序列式容器,其底层为线性序列的数据结构,里面存储的是数据本身。

关联式容器,其底层是红黑树(一种平衡搜索树),里面存储的是<key, val>结构的键值对,也是存储数据本身的。set和map均为关联式容器。

 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。举个例子,用过和没用过英汉字典的都知道,每个单词都有对应的中文翻译(我们假设每个单词和中文之间是一一对应的),当我们找到hello的时候,也就找到了它的中文翻译“你好”。形如<hello, 你好>这样,都可以看做是键值对。

以下是从SGI-STL中提取的关于键值对的定义,STL库中的实现远比这复杂,因为STL库要考虑的因素很多,我们只是抽出其核心。

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

//我们自己实现的pair核心功能是有的
int main()
{
	pcz::pair<string, string> p("left","左边");
	return 0;
}

set的介绍 

有了前面的铺垫以后,我们进入正题。想要准确了解set的特性,就避不开文档。下面我们先看看文档中是怎么介绍set的。这里多说一嘴,有些同学可能对这些英文文档很反感,但我想说的是计算机毕竟是老外搞出来的,很多主流的论坛比如stackoverflow等都是老外的,接入这些论坛我们必不可少要接触英语。所以呢,英语还是要好好重视。当然,下面我也用中文列出了文档中提到关于set的特性。

Sets are containers that store unique elements following a specific order.

1)set是按照特定顺序存储唯一元素的容器。


In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.
2)在set中,元素的值唯一标识它,key就是value,value就是key,而且每一个值都是唯一的。

3)set中元素的值不可修改,一旦元素进入set中,它就总是常量,但是可以从容器中插入和删除它们。 


Internally, the elements in a set are always sorted following a specific strict weak ordering criterion indicated by its internal comparison object (of type Compare).

4)在内部,set总是按照其内部比较对象所指示的特定严格弱排序准则进行排序。

set containers are generally slower than unordered_set containers to access individual elements by their key, but they allow the direct iteration on subsets based on their order.

5)set容器一般在通过key访问单个元素时比unordered_set容器慢,但是它们允许根据顺序对子集直接进行迭代。

Sets are typically implemented as binary search trees.

6)set通常是以二叉搜索树实现。

上面翻译完了,下面我们说一点细节:

> set在底层实际存放的是<value, value>构成的键值对,所以说key就是value,value就是key。

> set中插入数据时,只需要插入value即可,不需要构建键值对。

> set中的元素不可以重复,因此可以使用set去重。

> 使用set的迭代器遍历内部的元素,得到的是有序序列(中序)

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

> set中查找某个元素,时间复杂度为logn(以2为底),就相当于二分查找

> set的底层是用红黑树来实现的 

set的使用

有了上面的了解后,我们接下来看看set的使用,下面将介绍set中常用的函数。

set的构造

函数声明功能介绍
set (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
构造空的set
set (InputIterator first, InputIterator last,
       const key_compare& comp = key_compare(),
       const allocator_type& alloc = allocator_type());
用[first,last)区间中的元素构造set
set (const set& x);
set的拷贝构造
int arr[] = { 1, 2, 3, 4, 5 };

set<int> st1;									   //构造空的set
set<int> st2(arr, arr + sizeof(arr) / sizeof(int));//区间构造
set<int> st3(st2);								   //拷贝构造
set<int> st4(st2.begin(), st2.end());			   //迭代器区间构造

 set中的常用方法

> begin()   返回指向第一个元素的迭代器 

> end()     返回指向最后一个元素的后面理论元素的迭代器(下面我会单独解释此处)

> clear()   删除set中的所有元素,size置为0

> empty() 判断set是否为空,为空返回true,不为空返回false

> rbegin() 返回指向最后一个元素的迭代器

> rend() 返回指向第一个元素之前的理论元素的迭代器

> size()  返回set中元素的个数

> max_size() 返回set中可以容纳的最大元素个数,实际上可能并不一定可以容纳这么多

关于end()的解释:

我们来看看文档:

Returns an iterator referring to the past-the-end element in the set container.The past-the-end element is the theoretical element that would follow the last element in the set container.

大概是这么说的:end()返回一个指向the past-the-end元素的迭代器,the past-the-end元素是跟在最后一个元素后面的理论元素。

下面,我将being()、end(),rbegin()、rend()所对应的位置用一张图表示,希望对你有所帮助。

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

int main()
{
	int arr[] = { 1,5,9,2,7,3 };
	set<int> st1(arr, arr + sizeof(arr) / sizeof(int));

	cout << "第1个元素:" << *st1.begin() << endl;
	cout << "最后一个元素:" << *(--st1.end()) << endl;
	cout << "rbegin()返回值:" << *st1.rbegin() << endl;
	cout << "当前元素个数:" << st1.size() << endl;
	cout << "可容纳的最大元素个数:" << st1.max_size() << endl;

	st1.clear();
	cout << endl << "清楚所有元素后:" << endl;

	cout << "当前元素个数:" << st1.size() << endl;
	cout << "可容纳的最大元素个数:" << st1.max_size() << endl;

	return 0;
}

运行结果:
第1个元素:1
最后一个元素:9
rbegin()返回值:9
当前元素个数:6
可容纳的最大元素个数:214748364

清楚所有元素后:
当前元素个数:0
可容纳的最大元素个数:214748364

count

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

int main()
{
	int arr[] = { 1,5,9,2,7,3 };
	set<int> st1(arr, arr + sizeof(arr) / sizeof(int));

	cout << "5出现的次数:" << st1.count(5) << endl;
	cout << "8出现的次数:" << st1.count(8) << endl;

	return 0;
}

运行结果:
5出现的次数:1
8出现的次数:0

在set中,因为每一个值都是唯一的,所以count的返回结果只有两种:0或1。根据它的返回值,有时候也可以用count来判断一个元素是否在set中。

insert

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

将val插入到set中。如果插入成功,返回指向插入位置的迭代器和true。如果插入失败,也就是说set中已经存在要插入的val,那么将返回指向val的迭代器和false。

  void insert (InputIterator first, InputIterator last);

将区间[first,last)中的元素插入到set中。

③ iterator insert (iterator position, const value_type& val);

指定位置插入元素。说是怎么说,但是实际上并不一定按照我们指定的位置插入,而是会遵循搜索树的规则进行插入。

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

int main()
{
	int arr[] = { 1, 3, 2, 6, 7, 9 };
	set<int> st;
	set<int>::iterator it = st.begin();

	st.insert(it, 10);							    //指定位置插入
	st.insert(arr, arr + sizeof(arr) / sizeof(int));//区间插入
	st.insert(8);									//单个值插入

	cout << "set中的元素:";
	for (auto val : st)
	{
		cout << val << " ";
	}
	cout << endl;

	return 0;
}

运行结果:
set中的元素:1 2 3 6 7 8 9 10

erase

① iterator erase (const_iterator position);                                     删除指定位置的元素。

② size_type erase (const value_type& val);                                 删除指定元素,并返回删除的元素个数

③ iterator erase (const_iterator first, const_iterator last);             删除迭代器区间中的元素

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

int main()
{
	int arr[] = { 1, 5, 9, 2, 3, 8, 6, 0 };
	set<int> st(arr, arr + sizeof(arr) / sizeof(int));

	cout << "begin()指向的元素为:";
	cout << *st.begin() << endl;

	cout << "st.erase(st.begin())后剩余元素:";
	st.erase(st.begin());//删除指定位置的元素

	for (auto val : st)
		cout << val << " ";
	cout << endl;

	cout << "st.erase(9)后剩余元素:";
	st.erase(9);//删除指定元素

	for (auto val : st)
		cout << val << " ";
	cout << endl;

	set<int>::iterator first = st.begin();
	set<int>::iterator second = first;
	second++;
	second++;

	cout << endl;
	cout << "first:" << *first << "  " << "second:" << *second << endl;

	cout << "st.erase(first, second)后剩余元素:";
	st.erase(first, second);//删除迭代器区间内的元素
	for (auto val : st)
		cout << val << " ";
	cout << endl;

	return 0;
}

运行结果:
begin()指向的元素为:0
st.erase(st.begin())后剩余元素:1 2 3 5 6 8 9
st.erase(9)后剩余元素:1 2 3 5 6 8

first:1  second:3
st.erase(first, second)后剩余元素:3 5 6 8

find()

iterator find (const value_type& val);

如果找到指定值,则返回指定元素的迭代器;否则,返回值与end()的返回值一样,都是指向最后一个元素后面的理论元素。

以上就是关于set的介绍及使用,原本打算和map一块讲的,但是set占的篇幅远比想象的要多得多,继续讲map的话整体的篇幅就太长了,就先到这吧,下一篇再讲map。


完~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值