C++系列-STL容器之set和multiset


金陵酒肆留别 李白
风吹柳花满店香,吴姬压酒劝客尝。
金陵子弟来相送,欲行不行各尽觞。
请君试问东流水,别意与之谁短长。


set容器的特点

  • 是一种关联式容器,底层结构用二叉树。
  • 有序性:所有的元素插入时会被自动排序。
  • 唯一性:每个元素的值都是唯一的,不允许插入重复的值。
  • multiset可以有重复的值。

set容器的构造函数和赋值操作

构造函数解释
set()默认构造函数
set({elem1,elem2…})初始化列表构造函数
set(const set st2)拷贝构造函数
set(begin, end)迭代器范围构造函数
set<T,自定义比较函数的对象>()带比较器的构造函数
=赋值=运算符重载
code:
#include <iostream>
#include <vector>
#include <set>
using namespace std;

template<typename T>
void print_set(const set<T>& st)
{
	for (typename set<T>::const_iterator it = st.begin(); it != st.end(); it++)		// 不可以用 < end
	{
		cout << *it << " ";
	}
	cout << endl;
}

template<typename T1, typename T2>
void print_set(const set<T1, T2>& st)
{
	for (typename set<T1, T2>::const_iterator it = st.begin(); it != st.end(); it++)		// 不可以用 < end
	{
		cout << *it << " ";
	}
	cout << endl;
}

class MyCompare
{
public:
	bool operator()(int v1, int v2) const		// 这是需要用const限定,不然报错
	{
		return v1 > v2;
	}
};

void test01()
{
	// 构造函数举例
	set<int> st1{23, 1, 56, 78, 44, 35, 99, 76};		// 初始化列表构造函数
	cout << "---------- st, st.size() ---------- " << st1.size() << endl;
	print_set(st1);

	set<int> st2;			// 默认构造
	st2.insert(10);			// insert
	st2.insert(20);
	st2.insert(30);
	print_set(st2);

	set<int> st3(st2);		// 拷贝构造
	print_set(st3);

	vector<int> v1{1, 2, 3, 4, 5, 6};
	set<int> st4(v1.begin(), v1.end()-2);		// 迭代器范围构造
	print_set(st4);

	set<int, MyCompare> st5{23, 1, 56, 78, 44, 35, 99, 76};		//  带比较器的构造函数
	print_set<int, MyCompare>(st5);		// 重载

	set<int, MyCompare> st6 = st5;		// set赋值直接用=,set没有assign接口
	print_set(st6);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

result:
---------- st, st.size() ---------- 8
1 23 35 44 56 76 78 99
10 20 30
10 20 30
1 2 3 4
99 78 76 56 44 35 23 1
99 78 76 56 44 35 23 1

set容器的常用接口

set容器插入元素

  • set容器并没有push操作,因为set会自动排序,而push一般是增加元素在末尾位置,并不和逻辑。
  • set插入元素用insert接口。
接口函数说明
pair<iterator,bool> insert (const value_type& val)普通引用方式传参
pair<iterator,bool> insert (value_type&& val)右值引用方式传参
iterator insert (const_iterator position, const value_type& val)普通引用方式传参
iterator insert (const_iterator position, value_type&& val)以右值引用的方式传递 val 值
void insert (InputIterator first, InputIterator last)迭代器范围传参
void insert (initializer_list<value_type> il);初始化列表方式传参

普通引用方式传参和有值引用方式传参即()中的参数是一个立即释放的变量还是被定义过的变量。如int a, 如果a作为参数是普通引用方式传参,如果数值10作为参数,则为有值引用方式传参,因为实际中10只能用作右值,当前语句执行完,10就的内存就被释放。

对于set来说,插入的元素会被自动排序,那么insert接口中的pos参数是什么意思呢?它显然不是表示插入位置的。
pos不指定要插入的位置,它仅指向要开始搜索操作以插入的位置,以加快处理速度。

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

template<typename T>
void print_set(const set<T>& st)
{
	for (typename set<T>::const_iterator it = st.begin(); it != st.end(); it++)		// 不可以用 < end
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	set<int> st1{23, 1, 56, 78, 44, 35, 99, 76};	// 初始化列表构造函数
	cout << "---------- st, st.size() ---------- " << st1.size() << endl;
	print_set(st1);

	// insert举例
	
	// pair<iterator,bool> insert (const value_type& val)	// iterator是插入元素的迭代器,bool是插入元素是否成功
	int a = 100;
	pair<set<int>::iterator, bool> pr1 =st1.insert(a);
	print_set(st1);
	cout << "---------- pair<set<int>::iterator, bool> pr1 =st1.insert(a) ----------" << endl;
	cout << "* pr1.first: " << *pr1.first << ", pr1.second: " << pr1.second << endl;

	//  pair<iterator,bool> insert (value_type&& val) 		// 右值引用方式传参
	pair<set<int>::iterator, bool> pr2 = st1.insert(66);	// iterator是插入元素的迭代器,bool是插入元素是否成功
	print_set(st1);
	cout << "---------- pair<set<int>::iterator, bool> pr2 = st1.insert(66) ----------" << endl;
	cout << "* pr2.first: " << *pr2.first << ", pr2.second: " << pr2.second << endl;

	// iterator insert(const_iterator position, const value_type & val)
	set<int>::iterator it1 = st1.insert(st1.begin(), 88);	// iterator是插入元素的迭代器,bool是插入元素是否成功
	print_set(st1);
	cout << "---------- set<int>::iterator it1 = st1.insert(st1.begin(), 88) ----------" << endl;
	cout << "* it1: " << *it1 << endl;

	// iterator insert(const_iterator position, const value_type & val)
	// st1.find(99)返回的是99的interator,插入数值从此开始往后,加快了处理速度
	set<int>::iterator it2 = st1.insert(st1.find(99), a);	// iterator是插入元素的迭代器,bool是插入元素是否成功
	print_set(st1);
	cout << "---------- set<int>::iterator it2 = st1.insert(st1.find(99), a) ----------" << endl;
	cout << "* it2: " << *it2 << endl;

	// void insert(InputIterator first, InputIterator last) 迭代器范围传参
	vector<int> v1{11, 22, 55, 33, 99, 44};
	st1.insert(v1.begin(), v1.end());
	cout << "---------- st1.insert(v1.begin(), v1.end()) ----------" << endl;
	print_set(st1);

	// void insert (initializer_list<value_type> il) 初始化列表方式传参
	set<int> st3{111, 222, 333};
	st3.insert({ 888, 555, 666 });
	cout << "---------- st3.insert({ 888, 555, 666 }) ----------" << endl;
	print_set(st3);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

result:
---------- st, st.size() ---------- 8
1 23 35 44 56 76 78 99
1 23 35 44 56 76 78 99 100
---------- pair<set<int>::iterator, bool> pr1 =st1.insert(a) ----------
* pr1.first: 100, pr1.second: 1
1 23 35 44 56 66 76 78 99 100
---------- pair<set<int>::iterator, bool> pr2 = st1.insert(66) ----------
* pr2.first: 66, pr2.second: 1
1 23 35 44 56 66 76 78 88 99 100
---------- set<int>::iterator it1 = st1.insert(st1.begin(), 88) ----------
* it1: 88
1 23 35 44 56 66 76 78 88 99 100
---------- set<int>::iterator it2 = st1.insert(st1.find(99), a) ----------
* it2: 100
---------- st1.insert(v1.begin(), v1.end()) ----------
1 11 22 23 33 35 44 55 56 66 76 78 88 99 100
---------- st3.insert({ 888, 555, 666 }) ----------
111 222 333 555 666 888

set容器删除元素

接口函数说明
size_type erase (const value_type& val)删除 set 容器中值为 val 的元素,返回删除的个数,对于set来说,值为0或者1,0表示原set表示原set中无此元素
iterator erase (const_iterator position)删除 position 迭代器指向的元素
iterator erase (const_iterator first, const_iterator last)删除 [first,last) 区间内的所有元素
code:
#include <iostream>
#include <vector>
#include <set>
using namespace std;

template<typename T>
void print_set(const set<T>& st)
{
	for (typename set<T>::const_iterator it = st.begin(); it != st.end(); it++)		// 不可以用 < end
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	set<int> st1{23, 1, 56, 78, 44, 35, 99, 76};	// 初始化列表构造函数
	cout << "---------- st1, st1.size() ---------- " << st1.size() << endl;
	print_set(st1);

	// size_type erase (const value_type& val), 删除元素,返回删除的个数,set容器中此返回值为0或1
	int size_56 = st1.erase(56);
	int size_100 = st1.erase(100);
	cout << "---------- st1.erase(56) ----------" << size_56 << endl;
	cout << "---------- st1.erase(100) ----------" << size_100 << endl;

	// iterator erase (const_iterator position), 删除position处的元素,返回下一个迭代器
	set<int>::iterator it = st1.erase(st1.find(23));
	cout << "---------- set<int>::iterator it = st1.erase(st1.find(23)) ----------" << endl;
	print_set(st1);
	cout << "* it: " << *it << endl;

	set<int> st2{11, 22, 33, 44, 55, 66};
	cout << "---------- st2, st2.size() ---------- " << st2.size() << endl;
	print_set(st2);


	// iterator  erase(const_iterator first, const_iterator last), 删除[first, last) 区间内的所有元素 
	// 返回下一个元素的iterator
	it = st2.erase(st2.begin(), st2.find(44));
	cout << "---------- it = st2.erase(st2.begin(), st2.find(44)) ----------" << endl;
	print_set(st2);
	cout << "* it: " << *it << endl;

	// 删除set中的所有元素
	st2.clear();
	cout << "---------- st2.clear(), st2.size():  ---------- " << st2.size() << endl;
	print_set(st2);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

result:
---------- st1, st1.size() ---------- 8
1 23 35 44 56 76 78 99
---------- st1.erase(56) ----------1
---------- st1.erase(100) ----------0
---------- set<int>::iterator it = st1.erase(st1.find(23)) ----------
1 35 44 76 78 99
* it: 35
---------- st2, st2.size() ---------- 6
11 22 33 44 55 66
---------- it = st2.erase(st2.begin(), st2.find(44)) ----------
44 55 66
* it: 44
---------- st2.clear(), st2.size():  ---------- 0


其它接口

接口函数说明
iterator find(const key_type& _Keyval)返回指向所寻找的元素的iterator,没有该元素时,返回end()迭代器
size_type size()返回set的大小
bool empty()判断set是否为空
count(size_type count(const key_type& _Keyval))返回某个元素的数目,set容器为0或者1
code:
#include <iostream>
#include <vector>
#include <set>
using namespace std;

template<typename T>
void print_set(const set<T>& st)
{
	for (auto i_st : st)		// auto在这里表示自动类型推导,在这里也可以for (int i_st1: st1)
	{
		cout << i_st << " ";
	}
cout << endl;
}

void test01()
{
	set<int> st1{23, 1, 56, 78, 44, 35, 99, 76};	// 初始化列表构造函数
	cout << "---------- st1, st1.size() ---------- " << st1.size() << endl;
	print_set(st1);

	// iterator find(const key_type& _Keyval), 如果找到则返回指向该元素的迭代器,否则返回end()迭代器
	set<int>::iterator it;
	it = st1.find(22);
	if (it == st1.end())
	{
		cout << "st1.find(22), 找不到该元素" << endl;
	}
	else
	{
		cout << "* it: " << *it << endl;
	}

	it = st1.find(99);
	if (it == st1.end())
	{
		cout << "st1.find(99), 找不到该元素" << endl;
	}
	else
	{
		cout << "st1.find(99), * it: " << *it << endl;
	}
	// count(size_type count(const key_type& _Keyval)),返回某个元素的数目,set容器为0或者1|
	cout << "st1.count(78): " << st1.count(78) << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

result:
---------- st1, st1.size() ---------- 8
1 23 35 44 56 76 78 99
st1.find(22), 找不到该元素
st1.find(99), * it: 99
st1.count(78): 1

set和multiset的区别

元素的唯一性

  • set中的元素是唯一的,不允许有重复值。
  • multiset可以包含重复的元素。

插入操作

  • set如果插入已存在的元素,不会插入成功,容器的大小不会改变。
  • multiset可以多次插入相同的值,容器的大小会改变。

查找操作

  • set中要么找不到,要么只能找到一个。
  • multiset查找中可能会找到多个相同值的位置。

性能影响

  • set要保证元素的唯一性,在插入元素时可能需要进行多次的比较和调整操作,插入操作可能相对较慢。
  • multiset在插入重复元素时相对更高效一些,但在查找特定值时可能需要检查多个相同值的位置,影响查找性能。
  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值